Closed4

「Practical Deep Learning for Coders」Part1: 1. Getting started

kun432kun432

いろいろLLM周りをいじってはいるものの、Transformerとか見様見真似で動かしたりはするもの、きちんと学んでいないってのが常に自分の中ではコンプレックスとしてある。

手を動かしながら学べるものはないか?と、いろいろ調べているうちに以下の記事を見つけた。

https://zenn.dev/hijikix/articles/4ade637bec7ccb

恥ずかしながらHuggingFaceが公開しているNLPコースとか知らなかった。

https://huggingface.co/learn/nlp-course/en/chapter1/1

ただし、上記の記事ではその前段として「Practical Deep Learning for Coders」がオススメされているので、まずはこちらをやってみようと思う。

https://course.fast.ai/

基本的に以下の方針でやる

  • 上記コースではKaggleが前提になっているようだが、自分はColaboratoryでやろうと思う。
  • これがメインの目的ではないので、あまり細かいところは気にせず進める。うまくいかなかったらスキップするか、諦める。
  • 記述も最小限。
  • のんびりやる。
kun432kun432

ColaboratoryのランタイムはT4で。CPUでもできるけど、ファインチューニングする際の速度が違うので。

DuckDuckGo検索のパッケージインストール

!pip install duckduckgo_search
!pip freeze | grep -i duckduckgo
出力
duckduckgo_search==6.3.3

DuckDuckGoで、指定したキーワードの画像検索をおこなう関数。

from duckduckgo_search import DDGS
from fastcore.all import *
import time, json

def search_images(keywords, max_images=200):
    return L(DDGS().images(keywords, max_results=max_images)).itemgot('image')

鳥の写真で検索

urls = search_images('bird photos', max_images=1)
urls[0]
出力
https://images.pexels.com/photos/326900/pexels-photo-326900.jpeg?cs=srgb&dl=wood-flight-bird-326900.jpg&fm=jpg

画像をダウンロード

from fastdownload import download_url
from fastai.vision.all import *

dest = 'bird.jpg'
download_url(urls[0], dest, show_progress=False)

im = Image.open(dest)
im.to_thumb(256,256)

同様にして「森」の画像をダウンロード。たまにエラーになるが、何回か再実行すればいける。

download_url(
    search_images('forest photos', max_images=1)[0],
    'forest.jpg',
    show_progress=False
)
Image.open('forest.jpg').to_thumb(256,256)

学習用に複数の画像をまとめてダウンロード。

searches = 'forest','bird'
path = Path('bird_or_not')

for o in searches:
    dest = (path/o)
    dest.mkdir(exist_ok=True, parents=True)
    download_images(dest, urls=search_images(f'{o} photo'))
    time.sleep(5)
    resize_images(path/o, max_size=400, dest=path/o)

ダウンロードされたファイル数

!find bird_or_not -type f| wc -l
!find bird_or_not/bird -type f| wc -l
!find bird_or_not/forest -type f| wc -l
出力
324
161
163

参考までに、どういう画像がダウンロードされたかを見てみる。

import os
import matplotlib.pyplot as plt
from PIL import Image, UnidentifiedImageError

def display_image_grid(directory, rows=10, cols=10):
    """
    指定したディレクトリ内の画像をグリッドで表示します。

    Parameters:
    - directory (str): 画像ファイルが保存されているディレクトリのパス。
    - rows (int): グリッドの行数。デフォルトは10。
    - cols (int): グリッドの列数。デフォルトは10。
    """
    # 画像ファイルの一覧を取得
    image_files = [f for f in os.listdir(directory) if f.endswith(('png', 'jpg', 'jpeg'))]

    # グリッドを作成
    fig, axes = plt.subplots(rows, cols, figsize=(15, 15))
    plt.grid(False)  # グリッドを非表示

    # 各セルに画像を配置
    for i, ax in enumerate(axes.flat):
        if i < len(image_files):
            img_path = os.path.join(directory, image_files[i])
            try:
                with Image.open(img_path) as img:
                    ax.imshow(img)
                    ax.axis('off')  # 軸を非表示
                    ax.set_xticks([])  # x軸の目盛りを消す
                    ax.set_yticks([])  # y軸の目盛りを消す
            except (UnidentifiedImageError, IOError):
                ax.axis('off')  # 読み込めない画像はスキップ
        else:
            ax.axis('off')  # 画像がないセルを非表示

    plt.tight_layout()
    plt.show()

鳥の画像

display_image_grid("bird_or_not/bird")

森の画像

display_image_grid("bird_or_not/bird")

所々抜けているのはダウンロードに失敗しているケース。これらはトレーニングに失敗するので、削除する。

failed = verify_images(get_image_files(path))
failed.map(Path.unlink)
len(failed)

9ファイルが削除された

出力
10

再度ファイル数を確認しておく

!find bird_or_not -type f| wc -l
!find bird_or_not/bird -type f| wc -l
!find bird_or_not/forest -type f| wc -l
出力
314
156
158

fastaiのDataLoadersを使ってモデル学習用のデータセットオブジェクトを作成する

dls = DataBlock(
    # モデルの入力と出力を指定。入力は画像(ImageBlock)、出力はカテゴリー(CategoryBlock)
    blocks=(ImageBlock, CategoryBlock),
    # 入力データを取得するための関数を指定
    get_items=get_image_files,
    # データセットをvalidation用20%、training用残り、にランダムに分ける
    splitter=RandomSplitter(valid_pct=0.2, seed=42),
    # ラベルは入力の親フォルダとする、つまりbird / forest
    get_y=parent_label,
    # 画像を192x192になるように、スクイーズしてリサイズ
    item_tfms=[Resize(192, method='squish')]
).dataloaders(path, bs=32)

# データセットのサンプルを表示
dls.show_batch(max_n=6)

resnet18を使ってモデルをファインチューニングする。なお、一度試してみたのだけど、3エポックだとerror_rateが変わらなかったので

resnet18とは

ChatGPT調べ

ResNet18は、画像認識の分野で広く使われている畳み込みニューラルネットワーク(CNN)の一種で、「Residual Network(残差ネットワーク)」という意味です。特にResNet18は、18層の深さを持つResNetアーキテクチャのモデルを指します。

ResNetの特徴

ResNetは、2015年にMicrosoft Researchによって提案されたネットワークで、非常に深いニューラルネットワークの学習を効率的に行えるように設計されています。従来の深いネットワークでは、層が増えるほど誤差逆伝播の際に「勾配消失問題」が発生しやすくなりますが、ResNetは「残差ブロック(Residual Block)」を用いることでこの問題を解決しました。

残差ブロックとは

残差ブロックは、以下のように層を通過する際に入力がそのまま次の層へも直接渡される構造(ショートカット接続)を持っています。このショートカット接続により、層が深くなっても勾配消失が緩和され、学習が進みやすくなります。

  • 残差ブロックの基本構造:
    • 入力 → [畳み込み層 → 活性化関数 → 畳み込み層] → 出力
    • このとき、「入力」が直接「出力」に加算される

ResNet18の構造

ResNet18は、この残差ブロックを用いた18層のネットワークで、画像認識タスクで高い精度を実現しつつ、比較的軽量なモデルです。深層モデルの中でも計算負荷が比較的低く、画像分類のベースラインモデルとして利用されることが多いです。

まとめ

ResNet18は、次のような特徴があります。

  • 残差接続により勾配消失問題を軽減し、深いネットワークでも学習しやすい
  • 18層と比較的軽量で、扱いやすい
  • 画像認識の分野で広く使われており、画像分類や物体検出などで高い性能を発揮

興味があれば、さらに深い構造(ResNet34やResNet50)もあります。より深い構造にすることで認識精度を上げられる可能性がありますが、計算負荷も増加します。

learn = vision_learner(dls, resnet18, metrics=error_rate)
learn.fine_tune(3)

error_rateが0になっている。これで事前学習済みモデルの重みを自分のデータセットに合わせて「微調整」できたということになる。なお、事前学習済みモデルは、imagenetという1000カテゴリの画像を持つデータセットで、広く使用されているらしい。

では画像を識別してみる。notebookでは一番最初にダウンロードした画像を使用しているようだが、これは学習済みデータに含まれているように思うので、別の画像で試してみる。

Pixabayの以下の画像を使用させてもらう。アオサギかな?

https://pixabay.com/ja/photos/新しい-川-動物-鳥-3469961/

is_bird,_,probs = learn.predict(PILImage.create('new-3469961_640.jpg'))
print(f"This is a: {is_bird}.")
print(f"Probability it's a bird: {probs[0]:.4f}")
出力
This is a: bird.
Probability it's a bird: 0.6980
kun432kun432

初めて早速だけど、果たして続けるべきかなぁ・・・という感じ。時間が経ってしまっているところと、fastaiのライブラリに依存してるので、正直微妙な気がしている。

このスクラップは14日前にクローズされました