どうもニコイチです。前回(転移学習なしのモデル学習)では、転移学習を使わないモデルを学習させるところまで進めました。
今回は、その学習済みモデルを使って未知の画像データに対して推論(分類予測)を行う方法を学んでいきましょう。
推論用データの準備・整理
すでに用意してあるテストデータ(test_data.zip)を解凍し、その中に入っている画像ファイルを一覧化します。
推論に入る前に、以下のように未知データ(学習や検証で一度も見せていない画像データ) を整頓しておきましょう。
import zipfile
import pandas as pd
import os
def unzip_dataset(INPATH, OUTPATH):
    with zipfile.ZipFile(INPATH) as zf:
        zf.extractall(OUTPATH)
# zipファイルの解凍
unzip_dataset(INPATH='./test_data.zip', OUTPATH='./')
# 解凍されたフォルダに何が入っているか確認
print(os.listdir('./test_data/'))
解凍してできたフォルダ(./test_data/)の中には、正常品(ファイル名にok)と欠陥品(ファイル名にdef)が混在して入っています。
ファイル名一覧をDataFrameへ変換する
大量の画像を扱うときに便利なように、まずは画像のファイル名をまとめたDataFrame(df_test) を作成します。
df_test = pd.DataFrame(data=os.listdir('./test_data/'))
df_test = df_test.rename(columns={0: 'filename'})
df_test
これで、各画像のファイル名が1列目(filename)としてDataFrameにまとまりました。
正常品 or 欠陥品のラベルを付ける
学習時や評価時に「どの画像が正常品か、どの画像が欠陥品か」を管理しやすくするために、target という列を追加しておきます。
targetが 1 なら「正常品」targetが 0 なら「欠陥品」
df_test['target'] = 0
df_test.loc[df_test['filename'].str.contains('ok'), 'target'] = 1
str.contains('ok') でファイル名に “ok” が含まれているものを探し出し、そこだけ 1 に上書きしています。
最後に、このDataFrameをCSVファイルとして保存しておくと、後から簡単に参照できます。
df_test.to_csv("df_test.csv", index=False)
推論用のデータセットを作る【transforms】
PyTorchでデータを扱う際は、transforms → Dataset → DataLoader の3つでセットを作るのが基本です。
今回は推論用なので、下記のような前処理(transforms)を用意します。
- 画像サイズを 256×256 に統一 (
transforms.Resize(256)) - テンソル化 (
transforms.ToTensor()) - 正規化 (
transforms.Normalize([...], [...])) 
学習時と同じく、正規化の平均値や標準偏差は、[0.485, 0.456, 0.406] と [0.229, 0.224, 0.225] を使用します(ImageNetの推奨値)。
from torchvision import transforms
test_transforms = transforms.Compose([
    transforms.Resize(256),
    transforms.ToTensor(),
    transforms.Normalize([0.485, 0.456, 0.406],
                         [0.229, 0.224, 0.225]),
])
推論用のデータセットを作る【Dataset】
通常のImageFolderは、フォルダ構成(クラスごとに分かれている)が必要ですが、今回は「同じフォルダに正常と欠陥が混ざっている」ため、自作のDatasetクラスを使います。
このクラスでは、下記のように書きます。
__init__でdf_test.csvを読み込んでおく__len__でデータ数(行数)を返す__getitem__でインデックス番目の画像+ファイル名を返す
import pandas as pd
from torch.utils.data import Dataset
from PIL import Image
class Test_Datasets(Dataset):
    def __init__(self, data_transform):
        # CSV読み込み(columns指定で“filename”,“target”を読み取る)
        self.df = pd.read_csv('./df_test.csv', names=['filename','target'])
        self.data_transform = data_transform
    
    def __len__(self):
        # DataFrameの行数 = 画像データ数
        return len(self.df)
    
    def __getitem__(self, index):
        # 指定index番目のファイル名を取得
        file = self.df['filename'][index]
        # 画像を読み込み
        image = Image.open('./test_data/' + file)
        # 前処理(transforms)を適用
        image = self.data_transform(image)
        
        # 推論用なので返すラベルはファイル名程度でOK
        return image, file
推論用のデータローダを作る【DataLoader】
自作したDatasetを取り込み、1枚ずつ推論できるようにバッチサイズを1にしたDataLoaderを用意します。
Shuffleはしない(学習でも検証でもないためランダムに並べ替える必要がない)のが一般的です。
import torch
test_dataset = Test_Datasets(data_transform=test_transforms)
test_dataloader = torch.utils.data.DataLoader(
    dataset=test_dataset,
    batch_size=1,      # 画像1枚ずつ
    shuffle=False,     # シャッフルしない
    num_workers=0,     # シンプルに動かす
    drop_last=True     # もしバッチサイズに満たないデータがあっても切り捨てる
)
学習済みモデルのロード
「転移学習なし」で学習したモデルの重みをファイルから読み込みます。
そのためには、まずモデルの構造(型) が同じインスタンスを用意できるように、学習時と同じ関数や設定を再現する必要があります。
import torch.nn as nn
from torchvision import models
DEVICE = "cpu"
def get_model(target_num, isPretrained=False):
    # ResNet18をベースに最後の全結合層(FC)を出力数=2に付け替え
    model_ft = models.resnet18(pretrained=isPretrained)
    model_ft.fc = nn.Linear(512, target_num)
    model_ft = model_ft.to(DEVICE)
    return model_ft
# モデルインスタンスを作成
best_model = get_model(target_num=2)
# 学習済みのパラメータを読み込む
best_model.load_state_dict(
    torch.load('./original_model_33.pth', map_location=lambda storage, loc: storage),
    strict=True
)
print(best_model)
strict=True は、「全く同じ構造(層)を持つモデル」であればOK、という指定です。
もし微妙に構造が異なるなら strict=False を使わないとエラーが出ることがあります。
この辺りは、実際に転移学習の際によく遭遇するので、覚えておくと便利です。
学習済みモデルで未知データに対して推論
いよいよ、解凍した未知データ(./test_data/)を使って推論を行います。
流れは学習や検証のときと同じように、
- DataLoader から画像を取り出し
 - それをモデルに通し
 - 出力から予測ラベルを取り出す
 
というステップです。
pred = []  # 予測結果(0 or 1)を入れるリスト
# DataLoaderから画像を1枚ずつ取り出す
for i, (inputs, filenames) in enumerate(test_dataloader):
    inputs = inputs.to(DEVICE)
    
    # 推論モードに切り替え
    best_model.eval()
    # モデルに画像を通して出力を得る (出力は2次元ベクトル: [batch_size, 2])
    outputs = best_model(inputs)
    
    # 出力の中で最も値が大きい(確信度が高い)クラスのindex(0or1)を取得
    _, preds = torch.max(outputs, 1)
    
    # 予測結果をリストに追加
    pred.append(preds.item())
# 推論結果をDataFrameに追加
df_test = pd.read_csv("df_test.csv")  # 事前に作成しておいたdf_test.csvを読み込み
df_test['pred'] = pred
# 予測結果確認
print(df_test)
torch.max(outputs, 1)で求めるのは「最も値が大きいindex」です。- それが 
predsとして返ってきます。(0なら欠陥、1なら正常) 
こうして完成した df_test には、元々の正解ラベル(target)とモデルの予測ラベル(pred)が並ぶ形になります。
実際の製造現場では、これをもとに「どこで誤判定が多いか」「どういうパターンの画像が誤判定しやすいか」を検証して、さらなる改善を検討していきます。
まとめ
- 未知データの整理
- ZIPを解凍し、ファイル名とラベルをDataFrame(
df_test)で管理。 
 - ZIPを解凍し、ファイル名とラベルをDataFrame(
 - 推論用Dataset・DataLoaderの作成
- 学習時とほぼ同じ前処理(
transforms)を使う。 - 自作クラスでファイル名を1つずつ処理(
Test_Datasets)。 
 - 学習時とほぼ同じ前処理(
 - 学習済みモデルの読み込み&推論
best_model.load_state_dictで重みをロード。- DataLoaderから画像を取り出し、
best_modelで推論→出力の最大値が予測クラス。 
 
「転移学習を使わなくても学習・推論ができる」という流れは、ここでしっかり理解しておきたいポイントです。ただし、もし学習データが少ないと汎化性能(実際に役立つ精度)が足りずに終わる場合があります。その場合は次回以降で取り上げる転移学習を使うと飛躍的に精度を上げられることが多いです。
ワンポイント: 製造業界でのAI活用データ
たとえばPwCのレポート( “Global Artificial Intelligence Study” )によると、
2030年までにAIが世界経済に与える影響額は最大15.7兆ドルにもなる
といわれています。製造業に限ると、そのうちの数兆ドル規模を占めるとも試算され、
特に外観検査(画像解析)は多くの現場で導入が進んでいる分野です。
このように大手コンサルや調査会社が発表しているデータを参考にすると、AIを使った画像検査は実務的なニーズやメリットが非常に大きいことがわかります。
次回予告
次回は、
転移学習ありのモデルの選定と作成
に進み、より高い精度を目指す方法について解説していきます。
「データが少なくてもうまくいくの?」「精度をもっと上げたい」
そんなときは、ベースラインのモデル(学習済み重み)を活用する“転移学習” がとても強力な武器になります。
前向きに進めていきましょう。わたしは常に「必ず前進・成長していける」と信じています。

コメント