강의영상

- (1/4) Path 설명

- (2/4) 이미지 크롤링

- (3/4) 모형학습 및 결과분석

- (4/4) 테스트

import

from fastai.data.all import *
from fastai.vision.all import * 

Path

- 기능: 현재폴더, 혹은 그 하위폴더들에 속한 파일의 목록을 볼 수 있다.

path=Path() # Path클래스에서 인스턴스생성 
(path/'ghtop_images').ls()
(#2) [Path('ghtop_images/token.png'),Path('ghtop_images/sparknb.gif')]

- Path(...)에서 ...에 무엇을 넣느냐에 따라 원하는 경로를 설정할 수 있다.

path=Path('/home')
path.ls()
(#1) [Path('/home/cgb4')]

- 폴더를 만들수 있다.

path=Path() 
(path/'asdf').mkdir()
(path/'asdf').ls()
(#0) []

- 이미 폴더가 존재할 때는 아래와 같이 에러가 발생

(path/'asdf').mkdir()
---------------------------------------------------------------------------
FileExistsError                           Traceback (most recent call last)
/tmp/ipykernel_258436/283275367.py in <module>
----> 1 (path/'asdf').mkdir()

~/anaconda3/envs/bda2021/lib/python3.8/pathlib.py in mkdir(self, mode, parents, exist_ok)
   1286             self._raise_closed()
   1287         try:
-> 1288             self._accessor.mkdir(self, mode)
   1289         except FileNotFoundError:
   1290             if not parents or self.parent == self:

FileExistsError: [Errno 17] File exists: 'asdf'
(path/'asdf').mkdir(exist_ok=True)

- 생성한 폴더를 지우는 방법

(path/'asdf').rmdir()

이미지 크롤링

- 이미지 크롤링은 (1) 검색 (2) 이미지 주소를 찾음 (3) 해당주소로 이동하여 저장하는 과정을 반복하면 된다.

- 교재: 빙을 이용하여 이미지 크롤링

  • 단점: 애져에 가입, 완전무료가 아님 (학생에게 1년간 무료)

- 다른방법: 덕덕고를 이용한 이미지 크롤링

def search_images_ddg(key,max_n=200):
    """Search for 'key' with DuckDuckGo and return a unique urls of 'max_n' images
       (Adopted from https://github.com/deepanprabhu/duckduckgo-images-api)
    """
    url        = 'https://duckduckgo.com/'
    params     = {'q':key}
    res        = requests.post(url,data=params)
    searchObj  = re.search(r'vqd=([\d-]+)\&',res.text)
    if not searchObj: print('Token Parsing Failed !'); return
    requestUrl = url + 'i.js'
    headers    = {'User-Agent': 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:71.0) Gecko/20100101 Firefox/71.0'}
    params     = (('l','us-en'),('o','json'),('q',key),('vqd',searchObj.group(1)),('f',',,,'),('p','1'),('v7exp','a'))
    urls       = []
    while True:
        try:
            res  = requests.get(requestUrl,headers=headers,params=params)
            data = json.loads(res.text)
            for obj in data['results']:
                urls.append(obj['image'])
                max_n = max_n - 1
                if max_n < 1: return L(set(urls))     # dedupe
            if 'next' not in data: return L(set(urls))
            requestUrl = url + data['next']
        except:
            pass

- search_images_ddg(검색어)를 이용하여 검색어에 해당하는 url을 얻는다.

search_images_ddg('hynn',max_n=5)
(#5) ['https://yt3.ggpht.com/a/AGF-l7_1jF579BUaWHBEpY95iZAb0WI2SC4vykeo3A=s900-c-k-c0xffffffff-no-rj-mo','http://talkimg.imbc.com/TVianUpload/tvian/TViews/image/2020/03/21/GRMTjLNM9a88637203974033409433.jpg','https://images.genius.com/a37e8f087886e8a9f1f1d4d4d02aba44.960x960x1.jpg','https://www.nautiljon.com/images/people/01/59/hynn_99095.jpg?0','https://lastfm.freetls.fastly.net/i/u/770x0/f6744fc617da497938bf0560c82fe0d2.jpg#f6744fc617da497938bf0560c82fe0d2']

- download_images(저장하고싶은폴더위치, url의리스트)를 이용하여 url에 해당하는 이미지를 저장하고 싶은 폴더에 저장.

path=Path()
path.ls()
(#14) [Path('2021-09-06-cat2.jpeg'),Path('2021-09-06-hani03.jpg'),Path('2021-09-06-hani01.jpeg'),Path('ghtop_images'),Path('2021-09-07-(1주차) 9월7일.ipynb'),Path('Untitled.ipynb'),Path('2021-08-17-(A1) 깃허브와 fastpages를 이용하여 블로그 개설하기.ipynb'),Path('.ipynb_checkpoints'),Path('2021-09-02-(1주차) 9월2일.ipynb'),Path('2021-09-06-cat1.png')...]
download_images(path,urls=search_images_ddg('hynn',max_n=5))
  • 현재 working dir에 5개의 이미지가 저장된다.
keywords = 'hynn', 'iu' 
path=Path('singer')
if not path.exists(): # 현재폴더에 singer라는 폴더가 있는지 체크 
    path.mkdir() # 현재폴더에 singer라는 폴더가 만들어짐 
    for keyword in keywords: # keyword='hynn', keyword='iu' 일때 아래내용을 반복 
        lastpath=path/keyword # ./singer/hynn or ./singer/iu 
        lastpath.mkdir(exist_ok=True) # make ./singer/hynn or ./singer/iu 
        urls=search_images_ddg(keyword) # 'hynn' 검색어로 url들의 리스트를 얻음
        download_images(lastpath,urls=urls) # 그 url에 해당하는 이미지들을  ./singer/hynn or ./singer/iu 에 저장

Cleaning Data

- 탐색기로 파일들을 살펴보니 조금 이상한 확장자도 있음.

- 조금 이상해보이는 확장자도 열리기는 함.

PILImage.create('./singer/iu/00000006.jpg:large')
verify_images(get_image_files(path))
(#4) [Path('singer/iu/00000041.jpg'),Path('singer/iu/00000029.jpg'),Path('singer/iu/00000125.jpg'),Path('singer/hynn/00000077.png')]

- 위에 해당하는 이미지를 수동으로 지워줌.

- csv을 받았으면 df를 만들어야 하듯이, 이미지 파일들을 받았으면 dls를 만들어야 fastai가 지원하는 함수로 분석하기 좋다.

dls = ImageDataLoaders.from_folder(
    path,
    train='singer',
    valid_pct=0.2, 
    item_tfms=Resize(224))                                   
dls.show_batch(max_n=16)

- 모형을 만들고 학습을 시키자.

learn=cnn_learner(dls,resnet34,metrics=error_rate)
learn.fine_tune(7)
epoch train_loss valid_loss error_rate time
0 1.069038 0.753938 0.264706 00:04
epoch train_loss valid_loss error_rate time
0 0.638990 0.531955 0.220588 00:04
1 0.498534 0.338006 0.147059 00:04
2 0.392531 0.268666 0.132353 00:04
3 0.313377 0.214198 0.102941 00:04
4 0.262075 0.227022 0.088235 00:04
5 0.216234 0.228273 0.088235 00:04
6 0.192656 0.218852 0.088235 00:04
learn.show_results(max_n=16)

오답분석

interp = Interpretation.from_learner(learn)
interp.plot_top_losses(16)

- 수동으로 특정 observation에 대한 예측결과를 확인하여 보자.

dls.train_ds
(#272) [(PILImage mode=RGB size=960x960, TensorCategory(1)),(PILImage mode=RGB size=540x793, TensorCategory(1)),(PILImage mode=RGB size=800x1200, TensorCategory(1)),(PILImage mode=RGB size=720x960, TensorCategory(1)),(PILImage mode=RGB size=500x500, TensorCategory(0)),(PILImage mode=RGB size=1418x2000, TensorCategory(1)),(PILImage mode=RGB size=1920x1280, TensorCategory(1)),(PILImage mode=RGB size=480x360, TensorCategory(0)),(PILImage mode=RGB size=630x1045, TensorCategory(0)),(PILImage mode=RGB size=799x1200, TensorCategory(1))...]
  • training set
dls.train_ds[0]
(PILImage mode=RGB size=960x960, TensorCategory(1))
  • dls.train_ds[0] 가 의미하는 것은 첫번쨰 observation을 의미함. 즉 $(x_1,y_1)$
  • $x_1=$PILImage mode=RGB size=960x960
  • $y_1=$TensorCategory(1)
dls.train_ds[210][0]
  • $x_{211}$=위의 이미지
dls.train_ds[210][1]
TensorCategory(0)
  • $y_{211}=$TensorCategory(0)
x210=dls.train_ds[210][0]
learn.predict(x210)
('hynn', tensor(0), tensor([0.8893, 0.1107]))

Test

path = Path()
if not (path/'test').exists():
    (path/'test').mkdir()
urls=search_images_ddg('hynn 박혜원',max_n=20)
download_images(path/'test',urls=urls)
testset=get_image_files(path/'test')
testset
(#20) [Path('test/00000010.jpg'),Path('test/00000005.jpg'),Path('test/00000013.jpg'),Path('test/00000011.jpg'),Path('test/00000003.jpg'),Path('test/00000000.jpg'),Path('test/00000015.png'),Path('test/00000004.jpg'),Path('test/00000012.jpg'),Path('test/00000006.jpg')...]
for i in range(len(testset)): 
    print(learn.predict(PILImage.create(testset[i])))
('hynn', tensor(0), tensor([1.0000e+00, 1.5190e-06]))
('hynn', tensor(0), tensor([0.9516, 0.0484]))
('hynn', tensor(0), tensor([0.9904, 0.0096]))
('hynn', tensor(0), tensor([9.9952e-01, 4.7845e-04]))
('hynn', tensor(0), tensor([0.9990, 0.0010]))
('hynn', tensor(0), tensor([0.9983, 0.0017]))
('hynn', tensor(0), tensor([0.9923, 0.0077]))
('iu', tensor(1), tensor([0.1120, 0.8880]))
('hynn', tensor(0), tensor([0.9949, 0.0051]))
('hynn', tensor(0), tensor([0.9982, 0.0018]))
('hynn', tensor(0), tensor([0.9940, 0.0060]))
('hynn', tensor(0), tensor([1.0000e+00, 8.8760e-07]))
('hynn', tensor(0), tensor([0.9963, 0.0037]))
('hynn', tensor(0), tensor([9.9975e-01, 2.5230e-04]))
('hynn', tensor(0), tensor([0.7672, 0.2328]))
('hynn', tensor(0), tensor([9.9982e-01, 1.8401e-04]))
('hynn', tensor(0), tensor([1.0000e+00, 3.9835e-06]))
('hynn', tensor(0), tensor([1.0000e+00, 6.5406e-07]))
('hynn', tensor(0), tensor([0.9253, 0.0747]))
('iu', tensor(1), tensor([0.1957, 0.8043]))
  • 결과를 보니까 hynn이 많음 $\to$ 어느정도 맞추는것 같긴하다.
PILImage.create(testset[7])
  • 실제로는 박혜원인데 아이유로 예측한 사진
path = Path()
if not (path/'test2').exists():
    (path/'test2').mkdir()
urls=search_images_ddg('iu 아이유',max_n=20)
download_images(path/'test2',urls=urls)
testset=get_image_files(path/'test2')
testset
(#20) [Path('test2/00000010.jpg'),Path('test2/00000005.jpg'),Path('test2/00000013.jpg'),Path('test2/00000011.jpg'),Path('test2/00000003.jpg'),Path('test2/00000000.jpg'),Path('test2/00000004.jpg'),Path('test2/00000016.jpg'),Path('test2/00000009.jpeg'),Path('test2/00000012.jpg')...]
for i in range(len(testset)): 
    print(learn.predict(PILImage.create(testset[i])))
('iu', tensor(1), tensor([0.0051, 0.9949]))
('iu', tensor(1), tensor([8.7392e-06, 9.9999e-01]))
('iu', tensor(1), tensor([0.0895, 0.9105]))
('iu', tensor(1), tensor([0.0011, 0.9989]))
('iu', tensor(1), tensor([1.0321e-05, 9.9999e-01]))
('iu', tensor(1), tensor([0.0211, 0.9789]))
('iu', tensor(1), tensor([4.9877e-05, 9.9995e-01]))
('iu', tensor(1), tensor([0.0031, 0.9969]))
('iu', tensor(1), tensor([0.0011, 0.9989]))
('iu', tensor(1), tensor([1.5381e-05, 9.9998e-01]))
('iu', tensor(1), tensor([7.1447e-05, 9.9993e-01]))
('iu', tensor(1), tensor([1.3296e-04, 9.9987e-01]))
('hynn', tensor(0), tensor([0.9982, 0.0018]))
('iu', tensor(1), tensor([2.5169e-05, 9.9997e-01]))
('iu', tensor(1), tensor([1.2726e-05, 9.9999e-01]))
('iu', tensor(1), tensor([7.9650e-05, 9.9992e-01]))
('iu', tensor(1), tensor([3.0283e-04, 9.9970e-01]))
('iu', tensor(1), tensor([6.8668e-05, 9.9993e-01]))
('iu', tensor(1), tensor([0.0034, 0.9966]))
('iu', tensor(1), tensor([0.0052, 0.9948]))
  • 결과를 보니 아이유 역시 잘 맞추는 듯 보인다.

- 정확률이 아쉽긴 하지만 어느정도 유의미한 결과를 얻었다.

숙제

- 원하는 검색어로 이미지를 모은 뒤 결과를 제출