J'ai un énorme jeu de données d'image qui ne tient pas dans la mémoire. Je veux calculer les mean
et standard deviation
, en chargeant des images à partir du disque.
J'essaie actuellement d'utiliser cet algorithme trouvé sur wikipedia .
# for a new value newValue, compute the new count, new mean, the new M2.
# mean accumulates the mean of the entire dataset
# M2 aggregates the squared distance from the mean
# count aggregates the amount of samples seen so far
def update(existingAggregate, newValue):
(count, mean, M2) = existingAggregate
count = count + 1
delta = newValue - mean
mean = mean + delta / count
delta2 = newValue - mean
M2 = M2 + delta * delta2
return existingAggregate
# retrieve the mean and variance from an aggregate
def finalize(existingAggregate):
(count, mean, M2) = existingAggregate
(mean, variance) = (mean, M2/(count - 1))
if count < 2:
return float('nan')
else:
return (mean, variance)
Voici mon implémentation actuelle (informatique uniquement pour le canal rouge):
count = 0
mean = 0
delta = 0
delta2 = 0
M2 = 0
for i, file in enumerate(tqdm(first)):
image = cv2.imread(file)
for i in range(224):
for j in range(224):
r, g, b = image[i, j, :]
newValue = r
count = count + 1
delta = newValue - mean
mean = mean + delta / count
delta2 = newValue - mean
M2 = M2 + delta * delta2
print('first mean', mean)
print('first std', np.sqrt(M2 / (count - 1)))
Cette implémentation fonctionne assez bien sur un sous-ensemble du jeu de données que j'ai essayé.
Le problème est qu’il est extrêmement lent et donc non viable.
Y a-t-il un moyen standard de faire cela?
Comment puis-je adapter cela pour obtenir un résultat plus rapide ou calculer l'écart moyen et standard RVB pour tout le jeu de données sans tout charger en mémoire en même temps et à une vitesse raisonnable?
Comme il s’agit là d’une tâche très lourde en chiffres (beaucoup d’itérations autour d’une matrice ou d’un tenseur), je suggère toujours d’utiliser des bibliothèques performantes: numpy.
Un numpy correctement installé doit pouvoir utiliser les routines BLAS (sous-routines d'algèbre linéaire de base) sous-jacentes optimisées pour l'utilisation d'un tableau de points flottants de la perspective de la hiérarchie de la mémoire.
imread devrait déjà vous donner le tableau numpy. Vous pouvez obtenir le tableau 1d remodelé de l’image du canal rouge en
import numpy as np
val = np.reshape(image[:,:,0], -1)
la moyenne de tels par
np.mean(val)
et l'écart type par
np.std(val)
De cette façon, vous pouvez vous débarrasser de deux couches de boucles python:
count = 0
mean = 0
delta = 0
delta2 = 0
M2 = 0
for i, file in enumerate(tqdm(first)):
image = cv2.imread(file)
val = np.reshape(image[:,:,0], -1)
img_mean = np.mean(val)
img_std = np.std(val)
...
Le reste de la mise à jour incrémentielle devrait être simple.
Une fois que cela est fait, le goulot d'étranglement devient la vitesse de chargement de l'image, qui est limitée par les performances de la lecture du disque. À cet égard, j’imagine que l’utilisation de la technologie multi-thread, comme d’autres l’a suggéré, aidera beaucoup en me basant sur mon expérience antérieure.
Vous pouvez également utiliser la méthode d'opencv meanstddev .
cv2.meanStdDev(src[, mean[, stddev[, mask]]]) → mean, stddev
Si vous ne souhaitez pas mettre des éléments dans une mémoire avec un tableau contenant l'ensemble de vos données, vous pouvez simplement le calculer de manière itérative.
# can be whatever I just made this number up
global_mean = 134.0
# just get a per-pixel array with the vals for (x_i - mu) ** 2 / |x|
sums = ((images[0] - global_mean) ** 2) / len(images)
for img in images[1:]:
sums = sums + ((img - global_mean) ** 2) / len(images)
# Get mean of all per-pixel variances, and then take sqrt to get std
dataset_std = np.sqrt(np.mean(sums))