先日、「ProbSpace」というプラットフォームで開催された「浮世絵作者予測」コンペに参戦したので、振り返っていきたいと思います。
私にとって、画像分類のコンペは初めてでしたが、E資格の課題が画像分類でしたので、その時に得た知識をフル動員して取り組みました。
最終順位は参加232チーム中、10位でした。トップ10入りができてとてもうれしいです!!
まだまだ、改善の余地はたくさんあると思いますが、これまでの取り組みで結果を残せたので、素直に喜びたいと思います!!
全体像
上位の方の見よう見まねで全体像の図を作ってみました。
描き方とかも理解できていないので、わかりにくかったら申し訳ございません。
では、振り返ってまいります。
1.データ拡張
データを読み込んで、E資格の課題のときに大活躍したデータ拡張をいろんなモデルに、それぞれの割合をハイパーパラメータとして最大になる値を探索しました。
データ拡張は、HorizontalFlip,RandomContrast,RandomBrightness,Blur,Cutoutの6種類を使いました。
また、モデルはResNet18,34,101,WideResNet50,DenseNet121,VGG11,13,16の8種類を使いました。
以下は、ResNet18でのデータ拡張の割合を探索する例です。
import optuna
def objective(trial):model = models.resnet18(num_classes=10)model.cuda()
#最適化するパラメータの設定lr_par = trial.suggest_loguniform('learning_rate', 1e-5, 1e-2)HoriFlip_p = trial.suggest_uniform('H_Flip', 0.0, 0.5)RandCont_p = trial.suggest_uniform('R_Cont', 0.0, 0.5)RandBrig_p = trial.suggest_uniform('R_Brig', 0.0, 0.5)Cut_p = trial.suggest_uniform('Cut_out', 0.0, 0.5)#データを訓練用とテスト用に分けるtrain_x, valid_x, train_y, valid_y = train_test_split(train_images, train_labels, test_size=0.2, random_state=RANDOM_SEED)#データをローダーに格納する。train_loader = dataset.DataLoader(Dataset(train_x, train_y, HoriFlip_p, RandCont_p, RandBrig_p, Blur_p,Cut_p, train=True), batch_size=64, shuffle=True)valid_loader = dataset.DataLoader(Dataset(valid_x, valid_y, HoriFlip_p, RandCont_p, RandBrig_p, Blur_p, Cut_p,train=False), batch_size=64, shuffle=False)
if optim_flg == 'Adam':optimizer = optim.Adam(model.parameters(), lr=lr_par)else:
log = []
for epoch in range(20):model.train()
with autograd.detect_anomaly():train_loss, train_accuracy = perform(model, train_loader, optimizer)
model.eval()
with torch.no_grad():valid_loss, valid_accuracy = perform(model, valid_loader, None)
print('[{}] train(loss/accuracy)={:.2f}/{:.2f}, valid(loss/accuracy)={:.2f}/{:.2f}'.format(epoch + 1, train_loss, train_accuracy, valid_loss, valid_accuracy))
log.append( (epoch + 1, train_loss, train_accuracy, valid_loss, valid_accuracy) )
return valid_loss
#最適化の実行study = optuna.create_study()study.optimize(objective, n_trials=500)
2.1層目の学習
先ほど探索した割合のデータ拡張を施した入力を使って1層目の学習を実施します。
1層目の学習はStratifiedKFoldを使って、5Foldのクロスバリデーションを行いました。
今回は、各Fold毎に最も精度が良くなるモデルを保存するように工夫してみました。
def Exe_oof(model,origin_state, train_images,train_labels,test_images, optim_flg,epoch_num,str_name,num_acc,num_fold):cv = StratifiedKFold(n_splits=Nsplits, shuffle=True, random_state=42)oof_pred = np.zeros( (len(train_images),10 ) )y_pred_max = np.zeros( (len(test_images),10 ) )str_model = str_name +'_0_0000'y_pred_flg=0
for fold, (train_idx, val_idx) in enumerate(cv.split(train_images, train_labels)):print(fold)print(num_fold)if fold==num_fold:print('True')max_acc = num_accmodel.load_state_dict(origin_state)#データを訓練用と検証用に分けるtrain_x, valid_x = train_images[train_idx], train_images[val_idx]train_y, valid_y = train_labels[train_idx], train_labels[val_idx]#データをローダーに格納する。train_loader = dataset.DataLoader(Dataset(train_x, train_y, HoriFlip_p, RandCont_p, RandBrig_p, Blur_p,Cut_p, train=True), batch_size=64, shuffle=True)valid_loader = dataset.DataLoader(Dataset(valid_x, valid_y, HoriFlip_p, RandCont_p, RandBrig_p, Blur_p, Cut_p,train=False), batch_size=64, shuffle=False)
if optim_flg == 'Adam':optimizer = optim.Adam(model.parameters(), lr=lr_par)else:
log = []import datetime
print(datetime.datetime.now())
for epoch in range(epoch_num):
model.train()with autograd.detect_anomaly():train_loss, train_accuracy = perform(model, train_loader, optimizer)model.eval()with torch.no_grad():valid_loss, valid_accuracy = perform(model, valid_loader, None)
print('[{}] train(loss/accuracy)={:.2f}/{:.2f}, valid(loss/accuracy)={:.2f}/{:.2f}'.format(epoch + 1, train_loss, train_accuracy, valid_loss, valid_accuracy))
if valid_accuracy > max_acc:int_model = int(valid_accuracy*10000)str_model = str_name +'_'+ str(fold) +'_'+ str(int_model)DATA_DIR = 'drive/My Drive/ProbSpace/Ukiyoe/'+ str_name + '/' + str_modeltorch.save(model.state_dict(), DATA_DIR)################################## 検証用データの予測#################################model.evalhogehoge_i = 1for images, _ in valid_loader:images = images.cuda()with torch.no_grad():if hogehoge_i>=1 :preds = model(images)hogehoge_i=0else:preds = torch.cat([preds,model(images)],dim=0)dvs=torch.device('cpu')preds_cpu = preds.to(dvs)preds_np = preds_cpu.clone().numpy()oof_pred[val_idx] = preds_np
################################## テスト用データの予測#################################dummy_labels = np.zeros( (test_images.shape[0], 1), dtype=np.int64)test_loader = dataset.DataLoader(Dataset(test_images, dummy_labels,0.,0.,0.,0.,0.), batch_size=64, shuffle=False)model.eval()hogehoge_i = 1for images, _ in test_loader:images = images.cuda()with torch.no_grad():if hogehoge_i>=1 :preds = model(images)hogehoge_i=0else:preds = torch.cat([preds,model(images)],dim=0)y_pred_max = preds#################################max_acc = valid_accuracy
print(datetime.datetime.now())
log.append( (epoch + 1, train_loss, train_accuracy, valid_loss, valid_accuracy) )
if y_pred_flg==0 :y_pred = y_pred_maxy_pred_flg=1else:y_pred = torch.cat([y_pred,y_pred_max],dim=1)print('Partial score of fold {} is: {}'.format(fold, max_acc))
3.1層目の出力(2層目の入力を作成)
先ほど学習させた精度の良い各モデルの出力を2層目への入力として作成しました。
def make_input_for_layer2(model,train_images,train_labels,test_images,str_name,num_fold):cv = StratifiedKFold(n_splits=Nsplits, shuffle=True, random_state=42)for fold, (train_idx, val_idx) in enumerate(cv.split(train_images, train_labels)):print(fold)if fold==num_fold:print(True)#データを訓練用と検証用に分けるtrain_x, valid_x = train_images[train_idx], train_images[val_idx]train_y, valid_y = train_labels[train_idx], train_labels[val_idx]#データをローダーに格納する。valid_loader = dataset.DataLoader(Dataset(valid_x, valid_y, 0.0, 0.0, 0.0, 0.0, 0.0,train=False), batch_size=64, shuffle=False)
import datetimeprint(datetime.datetime.now())
if True:str_model = str_name +'_'+ str(fold)DATA_DIR = 'drive/My Drive/ProbSpace/Ukiyoe/'+ str_name + '/' + str_model################################## 検証用データの予測#################################model.evalhogehoge_i = 1for images, _ in valid_loader:images = images.cuda()with torch.no_grad():if hogehoge_i>=1 :preds = model(images)hogehoge_i=0else:preds = torch.cat([preds,model(images)],dim=0)dvs=torch.device('cpu')preds_cpu = preds.to(dvs)preds_np = preds_cpu.clone().numpy()print(preds_np.shape)################################## テスト用データの予測#################################dummy_labels = np.zeros( (test_images.shape[0], 1), dtype=np.int64)test_loader = dataset.DataLoader(Dataset(test_images, dummy_labels,0.,0.,0.,0.,0.), batch_size=64, shuffle=False)model.eval()hogehoge_i = 1for images, _ in test_loader:images = images.cuda()with torch.no_grad():if hogehoge_i>=1 :preds = model(images)hogehoge_i=0else:preds = torch.cat([preds,model(images)],dim=0)preds_sub = torch.max(preds, dim=1)[1]preds_cpu = preds.to(dvs)preds_y = preds_cpu.clone().numpy()print(preds_y.shape)print(preds.shape)#################################print(datetime.datetime.now())
with open('drive/My Drive/ProbSpace/Ukiyoe/'+ str_name + '/'+ str_model + '_preds_np.pkl','wb') as f1:pickle.dump(preds_np,f1)with open('drive/My Drive/ProbSpace/Ukiyoe/'+ str_name + '/'+ str_model + '_val_idx.pkl','wb') as f2:pickle.dump(val_idx,f2)with open('drive/My Drive/ProbSpace/Ukiyoe/'+ str_name + '/'+ str_model + '_pred_y_torch.pkl','wb') as f3:pickle.dump(preds,f3)with open('drive/My Drive/ProbSpace/Ukiyoe/'+ str_name + '/'+ str_model + '_pred_y.pkl','wb') as f4:pickle.dump(preds_y,f4)with open('drive/My Drive/ProbSpace/Ukiyoe/'+ str_name + '/'+ str_model + '_pred_sub.pkl','wb') as f5:pickle.dump(preds_sub,f5)with open('drive/My Drive/ProbSpace/Ukiyoe/Layer1/_'+ str_model + '_valid_y.pkl','wb') as f6:pickle.dump(valid_y,f6)
4.1層目の出力に係数をかけてLightGBMに入力
2層目は、LightGBMを使ってみました。
入力は、1層目の出力に対して、0.0~1.0の係数をかけて、精度が最大となる係数をOptunaにて探索しました。
5.上記までのモデルを3パターン作って平均をとる
最終的には、上記までのモデルを3パターン作って、平均したものを提出しました。
結果
最終結果としては、提出回数98回、ベストスコア:0.899、順位:10位でフィニッシュできました。
E資格の課題で、画像分類を実施していたので、その時の経験をフルに活用でき、ベスト10に入れたので、非常に満足のいく結果でした。
ただ、逆にいうとE資格の課題に取り組んだ際に学んだ知識しかなかったので、上位の方々の解法を参考にして、もっと精進していきたいと思います。
今後もいろいろなコンペに参加して、技術を磨いていきたいと思います。
【参考記事】E資格、G検定 合格記
2019年8月31日(土)にE資格を受験して、合格しました!
E資格対策として勉強の進め方や、参考書などをまとめました。
これから受験される方がいらっしゃいましたらご参考まで。
2019年3月9日(土)にG検定を受験し、見事合格できました!
受験の体験記や勉強法などを別のブログにまとめました。
これから受験される方がいらっしゃいましたらご参考まで。
【E資格対策に使った参考書】
- 人工知能は人間を超えるか ディープラーニングの先にあるもの (角川EPUB選書) [ 松尾豊 ]
- 深層学習教科書 ディープラーニング G検定(ジェネラリスト) 公式テキスト (EXAMPRESS) [ 一般社団法人日本ディープラーニング協会 ]
- 徹底攻略ディープラーニングG検定ジェネラリスト問題集 [ 明松真司 ]
- 実践機械学習システム [ ウィリ・リチャート ]
- アルゴリズムクイックリファレンス 第2版 [ George T. Heineman ]
- 深層学習【電子書籍】[ 岩澤 有祐 ]
- 入門Python 3 [ ビル・ルバノビック ]
- PythonによるWebスクレイピング 第2版 [ Ryan Mitchell ]
- Think Stats第2版 プログラマのための統計入門 [ アレン・B.ダウニー ]
- 集合知プログラミング [ トビー・セガラン ]
- ITエンジニアのための機械学習理論入門 [ 中井悦司 ]