PyTorchのCNNを使って漢字の画像を分類します。
1.漢字画像を生成する。
kanji.py
from PIL import Image, ImageDraw, ImageFont
import os
# 漢字リスト
kanji_list = ["山", "川", "木", "水", "火"] # 必要な漢字をリストに追加
output_dir = "kanji_images" # 画像を保存するディレクトリ
# フォント設定(フォントパスを適切に設定してください)
font_paths = [
"C:/Windows/Fonts/msmincho.ttc", # 明朝体(Windows用)
"C:/Windows/Fonts/meiryo.ttc"
]
# 画像生成用のパラメータ
image_size = (64, 64) # 画像サイズ(ピクセル)
font_size = 48 # フォントサイズ
# 出力ディレクトリの作成
os.makedirs(output_dir, exist_ok=True)
for kanji in kanji_list:
kanji_dir = os.path.join(output_dir, kanji)
os.makedirs(kanji_dir, exist_ok=True)
for font_path in font_paths:
try:
font = ImageFont.truetype(font_path, font_size)
for i in range(10): # 同じ漢字を10種類生成
img = Image.new("L", image_size, color=255) # グレースケール画像(背景白)
draw = ImageDraw.Draw(img)
# 漢字を画像の中央に描画
text_bbox = draw.textbbox((0, 0), kanji, font=font) # テキストのバウンディングボックスを取得
text_width = text_bbox[2] - text_bbox[0]
text_height = text_bbox[3] - text_bbox[1]
text_position = (
(image_size[0] - text_width) // 2,
(image_size[1] - text_height) // 2
)
draw.text(text_position, kanji, fill=0, font=font) # 黒で描画
# 画像の保存
img_path = os.path.join(kanji_dir, f"{kanji}_{i}.png")
img.save(img_path)
except Exception as e:
print(f"フォントの読み込みに失敗しました: {font_path} エラー: {e}")
print(f"漢字画像が生成されました。'{output_dir}'ディレクトリを確認してください。")
2.生成された漢字フォルダを data/train、data/valに振り分けておく。
3.CNNを使って漢字の画像を分類する。
pytorch_cnn.py
import torch
import torch.nn as nn
import torch.optim as optim
from torchvision import datasets, transforms
from torch.utils.data import DataLoader
import matplotlib.pyplot as plt
# デバイスの設定
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
# データ変換
transform = transforms.Compose([
transforms.Grayscale(num_output_channels=1), # 1チャネル(グレースケール画像)
transforms.Resize((64, 64)), # 画像サイズを統一
transforms.ToTensor(), # テンソル化
transforms.Normalize((0.5,), (0.5,)) # 正規化
])
# データセットの読み込み
train_dataset = datasets.ImageFolder(root="data/train", transform=transform)
val_dataset = datasets.ImageFolder(root="data/val", transform=transform)
train_loader = DataLoader(train_dataset, batch_size=32, shuffle=True)
val_loader = DataLoader(val_dataset, batch_size=32, shuffle=False)
# モデルの定義
class KanjiCNN(nn.Module):
def __init__(self, num_classes):
super(KanjiCNN, self).__init__()
self.conv_layers = nn.Sequential(
nn.Conv2d(1, 32, kernel_size=3, stride=1, padding=1), # 畳み込み層1
nn.ReLU(),
nn.MaxPool2d(kernel_size=2, stride=2), # プーリング層1
nn.Conv2d(32, 64, kernel_size=3, stride=1, padding=1), # 畳み込み層2
nn.ReLU(),
nn.MaxPool2d(kernel_size=2, stride=2) # プーリング層2
)
self.fc_layers = nn.Sequential(
nn.Flatten(),
nn.Linear(64 * 16 * 16, 128), # 全結合層1
nn.ReLU(),
nn.Dropout(0.5),
nn.Linear(128, num_classes) # 全結合層2
)
def forward(self, x):
x = self.conv_layers(x)
x = self.fc_layers(x)
return x
# モデルの初期化
num_classes = len(train_dataset.classes) # クラス数
model = KanjiCNN(num_classes).to(device)
# 損失関数と最適化アルゴリズム
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=0.001)
# 学習関数
def train_model(model, train_loader, val_loader, criterion, optimizer, num_epochs=10):
for epoch in range(num_epochs):
model.train()
train_loss = 0.0
for images, labels in train_loader:
images, labels = images.to(device), labels.to(device)
optimizer.zero_grad()
outputs = model(images)
loss = criterion(outputs, labels)
loss.backward()
optimizer.step()
train_loss += loss.item()
# 検証
model.eval()
val_loss = 0.0
correct = 0
total = 0
with torch.no_grad():
for images, labels in val_loader:
images, labels = images.to(device), labels.to(device)
outputs = model(images)
loss = criterion(outputs, labels)
val_loss += loss.item()
_, predicted = torch.max(outputs, 1)
total += labels.size(0)
correct += (predicted == labels).sum().item()
# 結果の出力
print(f"Epoch [{epoch+1}/{num_epochs}], "
f"Train Loss: {train_loss/len(train_loader):.4f}, "
f"Val Loss: {val_loss/len(val_loader):.4f}, "
f"Accuracy: {100 * correct / total:.2f}%")
# 学習の実行
train_model(model, train_loader, val_loader, criterion, optimizer, num_epochs=10)
4.実行結果
Epoch [1/10], Train Loss: 1.8352, Val Loss: 1.1979, Accuracy: 40.00%
Epoch [2/10], Train Loss: 1.4003, Val Loss: 0.9445, Accuracy: 80.00%
Epoch [3/10], Train Loss: 0.9725, Val Loss: 0.5208, Accuracy: 100.00%
Epoch [4/10], Train Loss: 0.5777, Val Loss: 0.1786, Accuracy: 100.00%
Epoch [5/10], Train Loss: 0.2621, Val Loss: 0.0478, Accuracy: 100.00%
Epoch [6/10], Train Loss: 0.1093, Val Loss: 0.0238, Accuracy: 100.00%
Epoch [7/10], Train Loss: 0.0564, Val Loss: 0.0038, Accuracy: 100.00%
Epoch [8/10], Train Loss: 0.0512, Val Loss: 0.0009, Accuracy: 100.00%
Epoch [9/10], Train Loss: 0.0394, Val Loss: 0.0003, Accuracy: 100.00%
Epoch [10/10], Train Loss: 0.0143, Val Loss: 0.0005, Accuracy: 100.00%