Klasyfikator obrazków z komiksów – deep learning #1.5

Zadaniem domowym, którym zakończyła się lekcja 1 deep learning było — stwórz własny klasyfikator obrazków. Postanowiłem więc w tym artykule podzielić się moją realizacją tego zadania.

Zbiór danych

Żeby się zabrać za to zadanie, potrzebny jest jakiś zbiór danych. Obrazków, których głównym elementem jest jakiś obiekt. Potrzebujemy więc x obrazków z obiektem a, y obrazków z obiektem b i z obrazków z obiektem c. W ten sposób pokażemy naszemu modelowi, że na obrazkach z katalogu a są obiekty a, z katalogu b są b i tak dalej. Nasz model nauczy się je odróżniać i dzięki temu uzyskami klasyfikator obrazków dopasowany do naszej potrzeby.

W materiałach do kursu uczestnicy przygotowali jakieś notatki, które ułatwiają web scraping obrazków z internetu. Znajdziesz je po prawej stronie w sekcji „How to scrape images” pod tym linkiem. Ja przez chwilę próbowałem korzystać z pierwszego pomysłu na wydobywanie ich z Google Images, ale poległem. Udało mi się jednak skorzystać z analogicznego systemu w DuckDuckGo. Pozbierałem trochę obrazków i wytrenowałem model. Nie podobało mi się jednak uzyskane rozwiązanie. A to dlatego, że wymyśliłem sobie, że zbuduję klasyfikator obrazków, który będzie specjalizował się w rozróżnianiu superbohaterów z dwóch największych wydawnictw komiksowych Marvel i DC.

Gdy wpisywałem nazwę superbohatera w wyszukiwarkę obrazków (obojętnie którą), to pojawiało się mnóstwo obrazków z komiksów, ale również z filmów i gadżetów. Np. jeśli chodziło o postać Marvela, która wystąpiła w filmach MCU, to stop-klatki stanowiły znaczną większość obrazków. Największym problemem było jednak wymieszanie tych obrazków. Okazało się, że dość ciężko jest w ten „automatyczny” sposób uzyskać dużo (>50) obrazków z pojedynczymi i właściwymi superbohaterami.

Ale od czego są społeczności geeków? Ano na przykład od tego, żeby katalogować komiksy. A wraz z katalogowaniem komiksów katalogują oni też bohaterów tych komiksów. A w jaki sposób najlepiej funkcjonują społeczności geeków katalogujących coś? Serwis wiki Fandom. A dokładniej Fandom DC i Fandom Marvel. Są to dwa różne serwisy oparte o technologię media wiki, które są prawdziwymi kopalniami wiedzy. I obrazków.

Każdy z superbohaterów ma swoją dedykowaną galerię. Tak wygląda galeria postaci Deadpool. Na pierwszy rzut oka widać, że większość to jeden bądź kilka kadrów z komiksów. Czyli dokładnie to o co mi chodziło. Fakt, są tam też inne grafiki i np. zdjęcia figurek, ale stanowią one bardzo mały ułamek całej galerii.

Web scraping

Okazuje się, że nie da się łatwo i intuicyjnie pobrać obrazków z tego typu galerii. Gdy klikniesz to, co wydaje się linkiem do obrazka, wyświetla się podstrona, na której jest link do właściwego obrazka. Ale okazuje się, że opracowano już sposób na pobieranie grafik z tego typu galerii. Jest on dokładnie opisany tutaj. Idea jest taka, że pobieramy wszystkie strony zalinkowanie ze wskazanej konkretnej strony. Następnie parsujemy te strony w poszukiwaniu adresu, który zawiera string „File:”. Wtedy znajdujemy tam też link do pełnego obrazka. Jak więc można się spodziewać, cały proces trochę trwa i większość czasu jest w nim poświęcone na pobieranie odpowiednich stron. Skrypt przerobiłem tak, żeby było mu obojętnie czy podaję mu galerię z DC, czy z Marvela:

#!/bin/bash

WIKI_URL=$1

if [ "$WIKI_URL" == '' ]; then
	echo "The first argument is the main webpage"
	echo
	exit 1
fi

PUBLISHER=$(echo $WIKI_URL | cut -f1 -d "." | cut -f3 -d "/")

# Download Image pages
echo "Downloading Image Pages"
wget -r -l 1 -e robots=off -w 1 -nc $WIKI_URL

# Extract Image Links
echo "Extracting Image Links"
WIKI_LINKS=`grep fullImageLink $PUBLISHER.fandom.com/wiki/File\:* | sed 's/^.*a href="//'| sed 's/".*$//'`

echo "Downloading Images"
wget -nc -w 1 -e robots=off -P downloaded_wiki_images $WIKI_LINKS

echo "Renaming:"
cd downloaded_wiki_images
for f in *; do mv "$f" "$f.jpg"; done
cd ..

echo "Cleaning up temp files"
rm -rf $PUBLISHER.fandom.com/
echo "Done"

exit

A, no i jeszcze nie rozgryzłem jak tworzyć poprawne nazwy obrazków. Na razie są tam identyfikatory z wiki i brak rozszerzenia. Ale w powyższym skrypcie dodaję do każdego obrazka rozszerzenie *.jpg. Faktem jest, że trafiają się tam też gify, ale nie przeszkadza to w całym ćwiczeniu.

Problemy

Namierzyłem do tej pory trzy problemy z uzyskanymi w ten sposób danymi.

Pierwszy problem to proporcje. Niektóre obrazki są bardzo poziome, a niektóre bardzo pionowe. Skrypty fastai radzą z tym sobie poprzez wycinanie maksymalnie dużego kwadratu ze środka obrazku. Problem jest jednak taki, że czasem na obrazku mamy postać od klatki piersiowej do kolan, albo kawałek „natury”. Wtedy klasyfikator pewnie się pomyli, bo będzie dość mocno zgadywał. W tym przypadku trzeba albo ręcznie przyciąć obrazki do kształtu postaci, albo użyć jakiegoś algorytmu wyszukiwania postaci.

Drugi problem to obrazki gdzie jest kilku superbohaterów. Prawie każdy obrazek zawiera jednego głównego superbohatera. Ale są też momenty w karierze takiego superbohatera, że odwiedza go „kolega po fachu”. Albo nawet ze sobą walczą, np. Hulk palony termicznym wzrokiem Supermana (swoją drogą srogi crossover). Wtedy obrazek jest podpisany nazwą galerii, z której pochodzi, a może okazać się, że gość jest lepiej widoczny. Wtedy pewnie klasyfikator rozpozna tam gościa, bo może rozpoznać tylko jedną postać. W tej sytuacji należałoby sprawdzić i usunąć takie obrazki ze zbioru.

Trzeci problem to alternatywne stroje, kolory, płcie, rasy, epoki, wymiary itp. Jeśli oglądałeś Spider-Man Uniwersum, to już się domyślasz, o co chodzi. W komiksach często dzieją się takie podmiany i są traktowane jako niekanoniczne wątki poboczne. Dobrze się sprzedają i pozwalają autorom fantazjować niemalże dowolnie. Mamy więc tutaj np. Petera Parkera przebranego za Punishera. Jest Batman jako Zielona/Żółta/Czarna/Biała/Czerwona Latarnia. Superman jako obrońca Związku Radzieckiego z sierpem i młotem zamiast „S” – czemu nie? Można tak wymieniać prawie w nieskończoność. Wiki Fandom są dość dobrze skatalogowane pod tym względem, czasem jednak w galerii pojawi się takie alter ego. Nie mówiąc już o tym, że superbohaterowie na ogół występują w swoich specjalnych strojach z maskami, jak i jako cywile. W tym momencie sieć też pewnie się pomyli. Najlepiej byłoby usunąć wszystkie niekanoniczne, cywilne i tymczasowe wersje postaci ze zbioru.

Model

Mój klasyfikator obrazków zbudowałem dokładnie tak samo, jak zostało to pokazane, we wspomnianej na początku lekcji. Nie wykazałem się tutaj żadną fantazją. Postanowiłem też nie czyścić ręcznie tych obrazków w celu wyeliminowania powyższych problemów. Zresztą, pewnie jak bym tak zrobił, to klasyfikator miałby prawie 100% skuteczności. A w ten sposób wprowadzam trochę chaosu do modelowania i zapewniam sobie dość ciekawe „pomyłki”.

Wyniki

Obrazki, które wrzuciłem do funkcji modelującej, zostały przeskalowane i zmniejszone. Dziewięć losowych obrazków treningowych wyglądało następująco:

Obrazki treningowe
Obrazki treningowe

Widzimy, że automatyczne przycinanie i skalowanie miało sens, oprócz obrazka numer 4 gdzie widzimy kawałek jakiejś sceny z cywilem, którzy coś prezentuje. Średnio to wygląda na Spider-Mana ;-).

Przy modelowaniu skorzystałem z wytrenowanego modelu resnet34. Najpierw 4 razy dotrenowałem go w wersji zamrożonej. W tym momencie error_rate wyniósł 0.376437, czyli klasyfikator mylił się w około 37 procentach przypadków. Chyba nieco za dużo. Następnie przejrzałem learning rate i zdecydowałem się wybrać przedział slice(1e-5,1e-3):

Learning Rate
Learning Rate

Swoją drogą, to przydałby się jakiś automatyczny sposób wybierania learning rate. Ale cóż, ogarnę to w przyszłości ;-).

Następnie odmroziłem model (odblokowałem zmiany w filtrach kształtów) i dotrenowałem go 32 razy. Teraz error_rate wyniósł 0.216475, czyli lepiej. Zerknijmy na tablicę pomyłek:

Tablica pomyłek
Tablica pomyłek

Jak przeczytać powyższą tablicę? Popatrzmy na podświetloną wartość: 6 obrazków, które naprawdę zawierają postać Deadpool, zostało uznane przez model za obrazki, na których jest postać Spider-Man.

10 największych wpadek modelu

Obejrzyjmy sobie po kolei 10 największych wpadek, które popełnił tak wytrenowany klasyfikator obrazków. Zaczniemy od tych gdzie strata była w miarę mała, aż do największej:

10

Wolverine
Batman?

Obrazek źródłowy tutaj. Wolverine w czarno — niebiesko — szarym stroju? W sumie to nie dziwne, że model się pomylił, wskazując go jako Batmana. Widać w sumie jego szpony, więc można by się spodziewać, że dobrze go rozpozna.

9

Deadpool
Wolverine?

Obrazek źródłowy tutaj. Okazuje się, że przy przygotowywaniu tej listy natrafiłem na czwarty problem z danymi. Powyższy obrazek pojawił się bowiem w galeriach Deadpoola i Wolverine. Wychodzi więc na to, że model dobrze rozpoznał obrazek ze zbioru testowego, na bazie opisu ze zbioru treningowego. Warto by więc zadbać o usunięcie powtarzających się obrazków.

8

Capitan America
Iron Man?

Obrazek źródłowy tutaj. Sytuacja niestety podobna jak powyżej. Jeden obrazek wylądował w dwóch galeriach. W zbiorze treningowym był podpisany jako Iron Man, a tutaj jako Capitan America. Ale przynajmniej dobrze wykrył Iron Mana ;-).

7

Spider-Man
Iron Man?

Obrazek źródłowy tutaj. Jakiś przypadkowy człowiek w garniturze i pod krawatem. Superbohater? Batman albo Iron Man. Na pewno nie Spider-Man, czyli Peter Parker. Sam go tutaj nie rozpoznałem.

6

Iron Man
Captain America?

Obrazek źródłowy tutaj. Ech, kolejny obrazek w dwóch klasach. Ale przynajmniej Captain America został dobrze zidentyfikowany :-).

5

Deadpool
Spider-Man?

Obrazek źródłowy tutaj. I znów poprawne wykrycie Spider-Mana na obrazku podpisanym Deadpool.

4

Wonder Woman
Batman?

Obrazek źródłowy tutaj. Zamaskowany koleżka w czarnej zbroi na czarno-białym obrazku. Niby jest tu Wonder Woman, ale na tym kadrze też bym jej nie rozpoznał. Faktycznie ciężki przypadek.

3

Superman
Wonder Woman?

Obrazek źródłowy tutaj. Hmm… ta postać jest podpisana jako SuperDoom, czyli jakaś kompletnie nieznana mi wersja Supermana. Niestety to zdjęcie pojawiło się też w wersji Wonder Woman, klasyfikator rozpoznał więc ją.

2

Iron Man
Hulk?

Obrazek źródłowy tutaj. Ogólnie to Iron Man wypadł z tego obrazka. Hulk go zdecydowanie zdominował. Był to co prawda duplikat, ale nawet gdyby nie był to nijak go tutaj nie widać. Wycinanie więc zrobiło nam psikusa.

1

Green Arrow?
Green Arrow?

Obrazek źródłowy tutaj. W sumie jak sobie popatrzę na obrazek źródłowy, to ten człowiek nijak nie wygląda na Kapitana Amerykę. Żółte włosy, bródka, zielony strój. Tak, to zdecydowanie komiksowy Green Arrow. Skąd taki Kapitan Ameryka to nie wiem.

Wnioski

Po napisaniu całego artykułu odkryłem, że mam duplikaty w zbiorze danych. Mam obrazki, na których jest kilka postaci komiksowych, które występują kilkukrotnie sprzecznie podpisane. I wychodzi na to, że jest to aktualnie największy problem tego klasyfikatora, bo prawie wszystko inne klasyfikuje zaskakująco dobrze. Jeśli w przyszłości poprawię ten zbiór i usunę z niego wszystkie pliki, które są w kilku katalogach, to efekt powinien być jeszcze ciekawszy. Okazuje się, że pozostałe problemy, które wymieniłem wcześniej, nie mają chyba tak dużego wpływu na jakość klasyfikatora. A czy Ty odrobiłeś już zadanie domowe?

Pełny kod przykładu i link do paczki z danymi znajdziesz tutaj.

Jeśli interesuje Cię jakiś temat – nie musi być związany z tym artykułem – to zostaw mi sygnał tutaj. Dzięki!

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *