Informacje o tym ćwiczeniu (w Codelabs)
1. Zanim zaczniesz
W tym ćwiczeniu z programowania będziesz używać splotu do klasyfikowania obrazów koni i ludzi. W tym module użyjesz TensorFlow, aby utworzyć sieć CNN wytrenowaną do rozpoznawania obrazów koni i ludzi oraz sklasyfikować je.
Wymagania wstępne
Jeśli nie udało Ci się jeszcze stworzyć rewolucji za pomocą TensorFlow, zalecamy ukończenie programów splotu i łączenie puli, gdzie wprowadzamy sploty i łączenie basenów. Polecamy budowanie splotowych sieci neuronowych (CNN) w celu poprawy wizji komputerów, w których omawiamy sposoby na zwiększenie efektywności rozpoznawania obrazów przez komputery.
Czego się nauczysz:
- Jak trenować komputery, aby rozpoznawały cechy obrazu, który jest nieostre
Co stworzysz
- Sieć neuronowa rewolucyjna, która pozwala odróżnić zdjęcia koni od zdjęć ludzi
Czego potrzebujesz
Kod do reszty ćwiczeń z programowania znajdziesz w Colab.
Musisz też mieć zainstalowane narzędzie TensorFlow, a także biblioteki zainstalowane w poprzednim kursie.
2. Pierwsze kroki: pozyskiwanie danych
Możesz to zrobić, tworząc klasyfikator konia lub człowieka, który poinformuje Cię, czy na danym obrazie widać konia lub człowieka. Sieć trenuje się tak, by rozpoznawać cechy decydujące o tym, który jest który. Zanim zaczniesz trenować, musisz trochę przetworzyć dane.
Najpierw pobierz dane:
!wget --no-check-certificate https://storage.googleapis.com/laurencemoroney-blog.appspot.com/horse-or-human.zip -O /tmp/horse-or-human.zip
Następujący kod w Pythonie używa biblioteki systemu operacyjnego w celu użycia bibliotek systemu operacyjnego, co zapewnia dostęp do systemu plików i biblioteki ZIP, co umożliwia rozpakowanie danych.
import os
import zipfile
local_zip = '/tmp/horse-or-human.zip'
zip_ref = zipfile.ZipFile(local_zip, 'r')
zip_ref.extractall('/tmp/horse-or-human')
zip_ref.close()
Zawartość pliku ZIP jest wyodrębniana do katalogu podstawowego /tmp/horse-or-human
, który zawiera konie i podkatalogi ludzkie.
Krótko mówiąc, zbiór treningowy to dane używane do informowania modelu sieci neuronowych, „jak wygląda koń” i „tak wygląda człowiek”."
3. Użyj programu ImageGenerator, aby dodać etykiety do danych i je przygotować
Obrazy nie mają wyraźnego oznaczenia koni ani ludzi.
Później zobaczysz nazwę ImageDataGenerator
. Odczytuje obrazy z podkatalogów i automatycznie oznacza je etykietami z podkatalogu. Załóżmy, że masz katalog szkoleniowy zawierający katalog koni i ludzi. ImageDataGenerator
odpowiednio oznaczy obrazy etykietami, co znacznie zmniejszy konieczność kodowania.
Zdefiniuj każdy z tych katalogów.
# Directory with our training horse pictures
train_horse_dir = os.path.join('/tmp/horse-or-human/horses')
# Directory with our training human pictures
train_human_dir = os.path.join('/tmp/horse-or-human/humans')
Zobacz, jak wyglądają nazwy plików w katalogach treningów koni i ludzi:
train_horse_names = os.listdir(train_horse_dir)
print(train_horse_names[:10])
train_human_names = os.listdir(train_human_dir)
print(train_human_names[:10])
Znajdź w katalogach łączną liczbę obrazów koni i ludzi:
print('total training horse images:', len(os.listdir(train_horse_dir)))
print('total training human images:', len(os.listdir(train_human_dir)))
4. Przeglądaj dane
Obejrzyj kilka zdjęć, aby lepiej zrozumieć, jak wyglądają.
Najpierw skonfiguruj parametry matplot
:
%matplotlib inline
import matplotlib.pyplot as plt
import matplotlib.image as mpimg
# Parameters for our graph; we'll output images in a 4x4 configuration
nrows = 4
ncols = 4
# Index for iterating over images
pic_index = 0
Wyświetl grupę 8 obrazów konnych i 8 zdjęć ludzkich. Możesz uruchomić komórkę ponownie, aby za każdym razem zobaczyć nową grupę.
# Set up matplotlib fig, and size it to fit 4x4 pics
fig = plt.gcf()
fig.set_size_inches(ncols * 4, nrows * 4)
pic_index += 8
next_horse_pix = [os.path.join(train_horse_dir, fname)
for fname in train_horse_names[pic_index-8:pic_index]]
next_human_pix = [os.path.join(train_human_dir, fname)
for fname in train_human_names[pic_index-8:pic_index]]
for i, img_path in enumerate(next_horse_pix+next_human_pix):
# Set up subplot; subplot indices start at 1
sp = plt.subplot(nrows, ncols, i + 1)
sp.axis('Off') # Don't show axes (or gridlines)
img = mpimg.imread(img_path)
plt.imshow(img)
plt.show()
Oto kilka przykładowych obrazów przedstawiających konie i ludzi w różnych pozycjach i orientacji:
5. Zdefiniuj model
Zacznij definiować model.
Zacznij od zaimportowania TensorFlow:
import tensorflow as tf
Następnie dodaj warstwy splotowe i spłaszczaj końcowy wynik, aby przesłać dane do gęsto połączonych warstw. Na koniec dodaj gęsto połączone warstwy.
Pamiętaj, że ponieważ klasyfikacja dwuklasowa (problem klasyfikacji binarnej) kończy Twoją sieć przy użyciu aktywacji sigmoidu, wynik wynikowy Twojej sieci będzie pojedynczą wartością skalarną z zakresu od 0 do 1. Koduje on prawdopodobieństwo, że bieżący obraz jest klasą 1 (a nie klasą 0).
model = tf.keras.models.Sequential([
# Note the input shape is the desired size of the image 300x300 with 3 bytes color
# This is the first convolution
tf.keras.layers.Conv2D(16, (3,3), activation='relu', input_shape=(300, 300, 3)),
tf.keras.layers.MaxPooling2D(2, 2),
# The second convolution
tf.keras.layers.Conv2D(32, (3,3), activation='relu'),
tf.keras.layers.MaxPooling2D(2,2),
# The third convolution
tf.keras.layers.Conv2D(64, (3,3), activation='relu'),
tf.keras.layers.MaxPooling2D(2,2),
# The fourth convolution
tf.keras.layers.Conv2D(64, (3,3), activation='relu'),
tf.keras.layers.MaxPooling2D(2,2),
# The fifth convolution
tf.keras.layers.Conv2D(64, (3,3), activation='relu'),
tf.keras.layers.MaxPooling2D(2,2),
# Flatten the results to feed into a DNN
tf.keras.layers.Flatten(),
# 512 neuron hidden layer
tf.keras.layers.Dense(512, activation='relu'),
# Only 1 output neuron. It will contain a value from 0-1 where 0 for 1 class ('horses') and 1 for the other ('humans')
tf.keras.layers.Dense(1, activation='sigmoid')
])
Wywołanie metody model.summary()
wyświetla podsumowanie sieci.
model.summary()
Wyniki możesz zobaczyć tutaj:
Layer (type) Output Shape Param # ================================================================= conv2d (Conv2D) (None, 298, 298, 16) 448 _________________________________________________________________ max_pooling2d (MaxPooling2D) (None, 149, 149, 16) 0 _________________________________________________________________ conv2d_1 (Conv2D) (None, 147, 147, 32) 4640 _________________________________________________________________ max_pooling2d_1 (MaxPooling2 (None, 73, 73, 32) 0 _________________________________________________________________ conv2d_2 (Conv2D) (None, 71, 71, 64) 18496 _________________________________________________________________ max_pooling2d_2 (MaxPooling2 (None, 35, 35, 64) 0 _________________________________________________________________ conv2d_3 (Conv2D) (None, 33, 33, 64) 36928 _________________________________________________________________ max_pooling2d_3 (MaxPooling2 (None, 16, 16, 64) 0 _________________________________________________________________ conv2d_4 (Conv2D) (None, 14, 14, 64) 36928 _________________________________________________________________ max_pooling2d_4 (MaxPooling2 (None, 7, 7, 64) 0 _________________________________________________________________ flatten (Flatten) (None, 3136) 0 _________________________________________________________________ dense (Dense) (None, 512) 1606144 _________________________________________________________________ dense_1 (Dense) (None, 1) 513 ================================================================= Total params: 1,704,097 Trainable params: 1,704,097 Non-trainable params: 0
Kolumna „Dane wyjściowe” pokazuje, jak rozmiar mapy cech zmienia się w poszczególnych warstwach. Warstwy splotu mogą nieznacznie zmniejszyć rozmiar map cech ze względu na dopełnienie, a każda warstwa basenu zmniejsza o połowę rozmiar.
6. Kompilowanie modelu
Następnie skonfiguruj specyfikacje do trenowania modelu. Wytrenuj model z stratą binary_crossentropy
, ponieważ jest to problem z klasyfikacją binarną, a ostateczna aktywacja jest sigmoidem. (Przypominamy o stratach w przypadku strat, które znajdziesz w artykule o zmniejszaniu się do systemów uczących się). Użyj optymalizatora rmsprop
z współczynnikiem nauczania 0,001. Podczas trenowania sprawdzaj dokładność klasyfikacji.
from tensorflow.keras.optimizers import RMSprop
model.compile(loss='binary_crossentropy',
optimizer=RMSprop(lr=0.001),
metrics=['acc'])
7. Wytrenuj model z generatorów
Skonfiguruj generatory danych, które będą odczytywać obrazy w folderach źródłowych, konwertować je na tensory pływające 32 i przesyłać do sieci (z ich etykietami).
Jeden generator obrazów treningowych i weryfikacyjnych. Twoje generatory będą generować grupy obrazów o rozmiarze 300 x 300 i ich etykietach (binarnych).
Dane, które trafiają do sieci neuronowych, powinny być w jakiś sposób znormalizowane, by ułatwić ich przetwarzanie przez sieć. (Niezbyt częste przesyłanie nieprzetworzonych pikseli do CNN). W tym przypadku wstępnie przetworzysz obrazy, normalizując wartości pikseli w zakresie [0, 1] (pierwotnie wszystkie wartości należą do zakresu [0, 255]).
W Keras można to zrobić za pomocą klasy keras.preprocessing.image.ImageDataGenerator
za pomocą parametru skalowania. Ta klasa ImageDataGenerator
umożliwia tworzenie instancji generatorów rozszerzonych zdjęć (i ich etykiet) za pomocą plików .flow(dane, etykiety) lub .flow_from_directory(katalog). Te generatory można następnie używać z metodami modelu Keras, które akceptują generatory danych jako dane wejściowe: fit_generator
, evaluate_generator
i predict_generator
.
from tensorflow.keras.preprocessing.image import ImageDataGenerator
# All images will be rescaled by 1./255
train_datagen = ImageDataGenerator(rescale=1./255)
# Flow training images in batches of 128 using train_datagen generator
train_generator = train_datagen.flow_from_directory(
'/tmp/horse-or-human/', # This is the source directory for training images
target_size=(300, 300), # All images will be resized to 150x150
batch_size=128,
# Since we use binary_crossentropy loss, we need binary labels
class_mode='binary')
8. Trenuj
Pociąg za 15 epoki. (może to potrwać kilka minut).
history = model.fit(
train_generator,
steps_per_epoch=8,
epochs=15,
verbose=1)
Zapisz wartości dotyczące epoki.
Wartości Utrata i Dokładność to świetne potwierdzenie postępów w szkoleniu. Zgaduje ona klasyfikację danych treningowych, a następnie porównuje je z znaną etykietą, obliczając wynik. Dokładność to część poprawnego odgadnięcia.
Epoch 1/15 9/9 [==============================] - 9s 1s/step - loss: 0.8662 - acc: 0.5151 Epoch 2/15 9/9 [==============================] - 8s 927ms/step - loss: 0.7212 - acc: 0.5969 Epoch 3/15 9/9 [==============================] - 8s 921ms/step - loss: 0.6612 - acc: 0.6592 Epoch 4/15 9/9 [==============================] - 8s 925ms/step - loss: 0.3135 - acc: 0.8481 Epoch 5/15 9/9 [==============================] - 8s 919ms/step - loss: 0.4640 - acc: 0.8530 Epoch 6/15 9/9 [==============================] - 8s 896ms/step - loss: 0.2306 - acc: 0.9231 Epoch 7/15 9/9 [==============================] - 8s 915ms/step - loss: 0.1464 - acc: 0.9396 Epoch 8/15 9/9 [==============================] - 8s 935ms/step - loss: 0.2663 - acc: 0.8919 Epoch 9/15 9/9 [==============================] - 8s 883ms/step - loss: 0.0772 - acc: 0.9698 Epoch 10/15 9/9 [==============================] - 9s 951ms/step - loss: 0.0403 - acc: 0.9805 Epoch 11/15 9/9 [==============================] - 8s 891ms/step - loss: 0.2618 - acc: 0.9075 Epoch 12/15 9/9 [==============================] - 8s 902ms/step - loss: 0.0434 - acc: 0.9873 Epoch 13/15 9/9 [==============================] - 8s 904ms/step - loss: 0.0187 - acc: 0.9932 Epoch 14/15 9/9 [==============================] - 9s 951ms/step - loss: 0.0974 - acc: 0.9649 Epoch 15/15 9/9 [==============================] - 8s 877ms/step - loss: 0.2859 - acc: 0.9338
9. Testowanie modelu
Teraz przeprowadź prognozę przy użyciu modelu. Umożliwia on wybranie co najmniej jednego pliku z systemu plików. Następnie zostanie on przesłany i przeanalizuje model, co wskazuje, czy jest to koń czy człowiek.
Obrazy z internetu możesz pobrać do systemu plików, aby je wypróbować. Pamiętaj, że sieć może popełnić wiele błędów, mimo że dokładność trenowania przekracza 99%.
Wynika to z naszego nadmiernego dostosowywania, co oznacza, że sieć neuronowa jest trenowana z bardzo ograniczonymi danymi (na każdej klasie jest tylko około 500 obrazów). Oznacza to, że bardzo nadaje się ona do rozpoznawania obrazów, które przypominają te z zestawu treningowego, ale często słabo radzą sobie z obrazami, które nie należą do zbioru treningowego.
To kolejny punkt potwierdzający, że im więcej danych trenujesz, tym lepsza będzie Twoja ostatnia sieć.
Istnieje wiele technik, które można wykorzystać do ulepszania treningu, mimo że ilość danych jest ograniczona. Należą do nich tzw. powiększanie obrazów, ale te wykraczają poza zakres tych ćwiczeń.
import numpy as np
from google.colab import files
from keras.preprocessing import image
uploaded = files.upload()
for fn in uploaded.keys():
# predicting images
path = '/content/' + fn
img = image.load_img(path, target_size=(300, 300))
x = image.img_to_array(img)
x = np.expand_dims(x, axis=0)
images = np.vstack([x])
classes = model.predict(images, batch_size=10)
print(classes[0])
if classes[0]>0.5:
print(fn + " is a human")
else:
print(fn + " is a horse")
Powiedzmy, że chcesz przetestować tę grafikę:
Oto, co zyskuje dzięki tej współpracy:
Mimo że jest to kreskówka, nadal jest poprawnie klasyfikowana.
Następujący obraz jest również poprawnie klasyfikowany:
Wypróbuj kilka własnych obrazów i odkrywaj!
10. Wizualizacja na poziomie pośrednim
Aby przekonać się, z jakich funkcji nauczyła się CNN, fajną rzeczą jest wizualizowanie tego, w jaki sposób dane są przetwarzane przez CNN.
Wybierz losowy obraz z zestawu treningowego, a następnie wygeneruj postać, w której każdy wiersz odpowiada wynikom warstwy, a każdy obraz w wierszu – określony filtr na tej mapie funkcji. Uruchom ponownie tę komórkę, aby wygenerować pośrednie reprezentacje dla różnych obrazów treningowych.
import numpy as np
import random
from tensorflow.keras.preprocessing.image import img_to_array, load_img
# Let's define a new Model that will take an image as input, and will output
# intermediate representations for all layers in the previous model after
# the first.
successive_outputs = [layer.output for layer in model.layers[1:]]
#visualization_model = Model(img_input, successive_outputs)
visualization_model = tf.keras.models.Model(inputs = model.input, outputs = successive_outputs)
# Let's prepare a random input image from the training set.
horse_img_files = [os.path.join(train_horse_dir, f) for f in train_horse_names]
human_img_files = [os.path.join(train_human_dir, f) for f in train_human_names]
img_path = random.choice(horse_img_files + human_img_files)
img = load_img(img_path, target_size=(300, 300)) # this is a PIL image
x = img_to_array(img) # Numpy array with shape (150, 150, 3)
x = x.reshape((1,) + x.shape) # Numpy array with shape (1, 150, 150, 3)
# Rescale by 1/255
x /= 255
# Let's run our image through our network, thus obtaining all
# intermediate representations for this image.
successive_feature_maps = visualization_model.predict(x)
# These are the names of the layers, so can have them as part of our plot
layer_names = [layer.name for layer in model.layers]
# Now let's display our representations
for layer_name, feature_map in zip(layer_names, successive_feature_maps):
if len(feature_map.shape) == 4:
# Just do this for the conv / maxpool layers, not the fully-connected layers
n_features = feature_map.shape[-1] # number of features in feature map
# The feature map has shape (1, size, size, n_features)
size = feature_map.shape[1]
# We will tile our images in this matrix
display_grid = np.zeros((size, size * n_features))
for i in range(n_features):
# Postprocess the feature to make it visually palatable
x = feature_map[0, :, :, i]
x -= x.mean()
if x.std()>0:
x /= x.std()
x *= 64
x += 128
x = np.clip(x, 0, 255).astype('uint8')
# We'll tile each filter into this big horizontal grid
display_grid[:, i * size : (i + 1) * size] = x
# Display the grid
scale = 20. / n_features
plt.figure(figsize=(scale * n_features, scale))
plt.title(layer_name)
plt.grid(False)
plt.imshow(display_grid, aspect='auto', cmap='viridis')
Oto przykładowe wyniki:
Jak widać, można wyjść z nieprzetworzonych pikseli obrazu aż do coraz bardziej abstrakcyjnych i kompaktowych obrazów. Reprezentacje reprezentujące dalszą część ścieżki wskazują, na co zwraca uwagę sieć, i są one coraz mniej aktywnych po aktywacji. Większość zera ma wartość zero. Nazywamy to parsity. Niewielka reprezentacja jest kluczowym elementem głębokiego uczenia się.
Odzwierciedlają one coraz mniej informacji o pierwotnych pikselach, ale dostarczają coraz bardziej szczegółowych informacji na temat klasy obrazu. Potok CNN (lub głęboka sieć w ogóle) można traktować jak potok destylacji informacji.
11. Gratulacje
Wiesz już, że można użyć CNN do poprawy złożonych obrazów. Aby dowiedzieć się, jak jeszcze bardziej ulepszyć swoje modele rozpoznawania obrazów, przeczytaj artykuł Korzystanie z sieci neuronowych splotowych (CNN) z dużymi zbiorami danych, aby uniknąć nadmiernego dopasowania.