ML Atlas

02 · Dane · 4 min czytania · Interaktywne · aktualizacja

Jak komputer widzi obraz i jak zamienić zdjęcie na dane dla modelu?

W skrócie

Dla komputera obraz to tablica liczb: jasności pikseli w kanałach kolorów. Spłaszczenie do wektora gubi sąsiedztwo pikseli, z którego korzystają sieci CNN.

Co to jest

Obraz cyfrowy to tablica liczb: każdy piksel ma wartość jasności, a obraz kolorowy — trzy wartości, po jednej dla kanału czerwonego, zielonego i niebieskiego (RGB). Zdjęcie 224 × 224 pikseli w kolorze to tablica o kształcie 224 × 224 × 3, czyli 150 528 liczb, zwykle całkowitych od 0 do 255.

Dla modelu uczenia maszynowego obraz jest więc po prostu bardzo długim wektorem cech. Różnica wobec danych tabelarycznych polega na tym, że te cechy mają strukturę: sąsiednie piksele są ze sobą silnie powiązane, a ten sam kształt może pojawić się w dowolnym miejscu kadru.

Intuicja: tabela z wiekiem i dochodem nie zmienia sensu, gdy zamienimy kolumny miejscami. Obraz z przemieszanymi pikselami przestaje być dla człowieka obrazem — choć zawiera dokładnie te same liczby.

Mechanizm — dlaczego tak działa

Najprostszy sposób użycia obrazu to spłaszczenie (flatten): tablicę 8 × 8 zamieniamy na wektor 64 liczb i podajemy klasycznemu modelowi — regresji logistycznej, SVM, k najbliższym sąsiadom. Działa to na małych, wyśrodkowanych obrazach, ale ma fundamentalną wadę: model nie wie, które piksele są sąsiadami. Dla niego piksel 12 i 13 są tak samo „daleko” jak 12 i 60. Każde przesunięcie obiektu o piksel zmienia, które cechy są zapalone, więc model musi osobno nauczyć się każdego położenia.

Druga trudność to wymiar. Nawet małe zdjęcie ma dziesiątki tysięcy cech, a przestrzeń możliwych obrazów jest astronomicznie duża. Prawdziwe obrazy zajmują w niej jednak cienką, uporządkowaną część — zgodnie z hipotezą rozmaitości (manifold hypothesis) — i dobra architektura potrafi ją wykorzystać.

Sieci konwolucyjne (CNN) wprowadzają do modelu wiedzę o strukturze obrazu. Filtr konwolucyjny patrzy na mały fragment (np. 3 × 3) i przesuwa się po całym obrazie z tymi samymi wagami. Daje to dwie własności: lokalność (cechy budowane są z sąsiednich pikseli) i współdzielenie wag (detektor krawędzi działa tak samo w każdym miejscu). Stąd ekwiwariancja względem przesunięć i ogromna oszczędność parametrów w porównaniu z warstwą w pełni połączoną.

Przygotowanie obrazów zwykle obejmuje: skalowanie wartości do zakresu [0, 1] lub standaryzację kanałów (średnia i odchylenie liczone na zbiorze treningowym), ujednolicenie rozmiaru, a podczas treningu — augmentację danych. W PyTorch kolejność wymiarów to zwykle kanały × wysokość × szerokość, w wielu innych bibliotekach wysokość × szerokość × kanały; pomylenie tego to częsty błąd.

Na przykładzie

Zbiór Digits z scikit-learn zawiera 1797 odręcznie pisanych cyfr, każdą jako obraz 8 × 8 pikseli w skali szarości z wartościami od 0 do 16. Klasy są zrównoważone (174–183 przykładów na cyfrę). Prawie połowa wartości (48,9%) to zera — tło — a 3 piksele są zerowe na wszystkich obrazach. Pierwsza cyfra, zero, wygląda w liczbach jak owal: wysokie wartości (13–15) po bokach, zera w środku i w rogach.

Regresja logistyczna na spłaszczonych 64 pikselach osiąga w 5-krotnej walidacji krzyżowej 91,4%, a k najbliższych sąsiadów (k = 3) — 96,7%. Gdy losowo przemieszamy kolejność pikseli (tę samą permutację dla każdego obrazu), wyniki się nie zmieniają: 91,5% i 96,7%. To dowód, że te modele w ogóle nie korzystają z układu przestrzennego. Za to przesunięcie obrazów testowych o jeden piksel w prawo obniża trafność regresji logistycznej z 96,1% do 46,3%, a o dwa piksele — do 8,7%, poniżej zgadywania. Model nauczył się, które piksele są zapalone, a nie jaki kształt widzi.

Ta ilustracja działa w przeglądarce z włączonym JavaScriptem: sieć 64→32→10 uczy się rozpoznawać ręcznie pisane cyfry 8×8 w przeglądarce; po 10 epokach trafia w około 95% cyfr testowych, a macierz pomyłek pokazuje, które cyfry myli.

Dane: Digits (ręcznie pisane cyfry 8×8)

W praktyce

  • Wczytywanie: PIL.Image.open + np.asarray, w PyTorch torchvision.io.read_image lub torchvision.transforms.ToTensor() (skaluje do [0, 1] i zmienia kolejność na C × H × W).
  • Spłaszczanie dla klasycznych modeli: images.reshape(n, -1); w scikit-learn Digits ma gotowe data (1797 × 64) i images (1797 × 8 × 8).
  • Normalizacja: transforms.Normalize(mean, std) z wartościami policzonymi na zbiorze treningowym.
  • Dla zdjęć większych niż kilkadziesiąt pikseli używaj sieci konwolucyjnych lub gotowych modeli z transfer learningiem.
  • Sprawdzaj typ danych: uint8 (0–255) przy odejmowaniu się „zawija”, więc przed obliczeniami rzutuj na float32.
  • Typowy błąd: pomylenie kolejności kanałów (RGB vs BGR w OpenCV) lub wymiarów (C × H × W vs H × W × C).

Najczęstsze pytania

Dlaczego nie wystarczy zwykła sieć w pełni połączona?
Wystarczy dla małych, wyśrodkowanych obrazów, ale nie wie, które piksele sąsiadują, i ma ogromną liczbę wag. Dla obrazu 224 × 224 × 3 jedna warstwa z 1000 neuronów to ponad 150 milionów parametrów, a każde przesunięcie obiektu trzeba uczyć osobno.
Czy obrazy trzeba normalizować?
Praktycznie zawsze. Wartości 0–255 utrudniają optymalizację gradientową; skalowanie do [0, 1] lub standaryzacja kanałów stabilizuje uczenie. Dla gotowych modeli trzeba użyć tej samej normalizacji, z jaką je trenowano.
Czy kolor jest potrzebny?
Zależy od zadania. Dla cyfr czy rentgenów wystarcza skala szarości (jeden kanał), dla rozpoznawania owoców czy znaków drogowych kolor niesie ważną informację.

Źródła

  • Goodfellow I., Bengio Y., Courville A. „Deep Learning”. MIT Press, 2016, rozdz. 9 (Convolutional Networks) i 12.2 (Computer Vision).
  • LeCun Y., Bottou L., Bengio Y., Haffner P. (1998). „Gradient-based learning applied to document recognition”. Proceedings of the IEEE, 86(11), 2278–2324.
  • Zhang A., Lipton Z. C., Li M., Smola A. J. „Dive into Deep Learning”, d2l.ai, rozdz. „Convolutional Neural Networks”.
  • Dokumentacja scikit-learn: Toy datasets — Optical recognition of handwritten digits, https://scikit-learn.org/stable/datasets/toy_dataset.html

Zobacz też