ãµãŒããµã€ããšã³ãžãã¢ã®æŸæš ( @tatsuma_matsuki ) ã§ããSafieè§£æãã©ãããã©ãŒã ãSafie APIã®éçºãäž»ã«æ
åœããŠããŸãã ãã®èšäºã¯ Safie Engineers' Blog! Advent Calendar 4æ¥ç®ã®èšäºã§ãã è§£æãã©ãããã©ãŒã ã§ã¯ãåŠç¿æžã¿ã¢ãã«ããã³ã©ã³ã¿ã€ã ãSafieã¯ã©ãŠãäžã«ç»é²ããããšã§ãSafieãµãŒãã¹ã§é²ç»ããã«ã¡ã©ã®æ åã»ç»åã«å¯ŸããŠãä»»æã®æšè«åŠçãå®è¡ãããã®å®è¡çµæãã€ãã³ããšããŠä¿åããããšãã§ããŸãã ãŸã ãµãŒãã¹å
¬éã¯ãããŠããŸãããããŸãã¯ç€Ÿå
ãšã³ãžãã¢ã§å©æŽ»çšã®æšé²ããããšããæããããSafieè§£æãã©ãããã©ãŒã ãå©çšããã¢ããªã±ãŒã·ã§ã³ã®éçºãå®è·µããŠããŸãã ç§ã¯ãµãŒããµã€ãã®ãšã³ãžãã¢ã§ãæ©æ¢°åŠç¿ã®åéã«ã¯æãããªãã®ã§ãããä»åè§£æãã©ãããã©ãŒã äžã§ã®ã¢ããªæ§ç¯ãäžããèªåã§éçºå¯èœã«ããããã«ãåŠç¿æžã¿ã¢ãã«ã®äœæãšæšè«åŠçã®å®è£
ãåããŠãã£ãŠã¿ãŸããïŒ çµæãšããŠããªããªãé«ã粟床ãåºããŸã§ã¯ãŸã å°ãæéããããããã§ããããã®äœæ¥ã»éçºå·¥çšããã®èšäºã§å
±æããããšæããŸãã éçºããã¢ããªã±ãŒã·ã§ã³ ç»åãåç»ã®åéã¯ç°¡åïŒ åŠç¿ïŒã¢ãã«äœæïŒ ããŒã¿ã®åå² ããŒã¿ã®æ¡åŒµ ããŒã¹ã¢ãã«ã®èªã¿èŸŒã¿ åŠç¿ã®å®è¡ æšè«åŠçã®å®è¡ 粟床ã®è©äŸ¡ AIã¢ãã«äœæã¯ç°¡åã§ã¯ãªãã£ã éçºããã¢ããªã±ãŒã·ã§ã³ ãŸãã¯é©åœã«æãã€ãããã®ãéçºããŠã¿ããïŒãšããããšã§ããªãã£ã¹ã«ããåŒç€Ÿã®CTOãã«ã¡ã©ã«åã£ããæ€ç¥ããŠãéç¥ïŒã¡ãŒã«ã»SlackïŒãåºãããã«ããããšãç®æšãšããŸããã åŒç€Ÿã®äºåç°ãªãã£ã¹ã¯ã4Fã5Fã8Fã®3ããã¢ãããåæå¯Ÿè±¡ãšããŠäœ¿ããããªã«ã¡ã©ã10å°åŒ±ã»ã©èšçœ®ãããŠããŸããæ§ã
ãªã¢ã³ã°ã«ã倧ããã§äººãåããã¿ãŸãããå
šãŠã®ã«ã¡ã©ã§æ±çšçã«äœ¿çšã§ããã¢ããªã±ãŒã·ã§ã³ïŒã¢ãã«ïŒãäœæã§ããã°ãã¹ãã§ãã ç»åãåç»ã®åéã¯ç°¡åïŒ Safie瀟å
ã§åŠç¿çšã®ç»åãéããã®ã¯ãšãŠãç°¡åã§ãïŒ ãªãã£ã¹å
ã«ã«ã¡ã©ãæ¢ã«åžžæçšŒåããŠãããéå»ã®æ åãå
šãŠç¢ºèªã§ãããããSafie Viewerããããããããã ãã§ç»åãã©ãã©ãéãŸããŸãã Safie Viewerã®ã¹ããªãŒãã³ã°ç»é¢ãéããŠãå³äžã®ã«ã¡ã©ã®ã¢ã€ã³ã³ãã¯ãªãã¯ãããšãã®æã«åã£ãŠããç»åã®ã¹ãããã·ã§ãããååŸã§ããŸãã å®è³ª1ïœ2æéã»ã©ã§ãæ§ã
ãªã«ã¡ã©ããåèš400æã»ã©ã®åŠç¿çšç»åãéãŸããŸããã åŠç¿ïŒã¢ãã«äœæïŒ ä»åéçºããã¢ããªã±ãŒã·ã§ã³ã®å Žåãå
¥åç»åããCTOãæ ã£ãŠããããCTOãåã£ãŠããªããã®äºã€ã«åé¡ããã¢ãã«ãäœæããå¿
èŠããããŸãã Efficientnetãšããæåãªåé¡ç³»ã®ã¢ãã«ãããŒã¹ã¢ãã«ãšããŠãã¡ã€ã³ãã¥ãŒãã³ã°ããŸãã arxiv.org ãŸãã以äžã®Kaggleã®notebookãåèã«ããŠãåŠç¿åŠçéšåãå®è£
ããŸããã www.kaggle.com 以äžã§ã¯ããã®åŠçå
容ããã£ãããšé ã远ã£ãŠèª¬æããŸãã ããŒã¿ã®åå² ãŸãã scikit-learn ã® train_test_split 颿°ã䜿ã£ãŠãéããç»åãçšéããšã«åå²ããŸãã ä»åã¯ã70%ã®ç»åãåŠç¿ããŒã¿ã15%ã®ç»åãè©äŸ¡ããŒã¿ãæ®ãã®15%ããã¹ãçšããŒã¿ãšããŠã©ã³ãã ã«åå²ããŸããã filepaths = [] labels = [] # `label_file_path`ã§æå®ãããã¹ã«ã¯ã<ç»åãã¡ã€ã«ã®ãã¹>,<ã©ãã«> ãåæããã # CSVãã¡ã€ã«ã眮ãããŠããŸãã with open (label_file_path) as f: reader = csv.reader(f) for row in reader: filepaths.append(row[ 0 ]) labels.append(row[ 1 ]) f_series = pd.Series(filepaths, name= "filepaths" ) l_series = pd.Series(labels, name= "labels" ) df = pd.concat([f_series, l_series], axis= 1 ) train_df, tmp_df = train_test_split( df, train_size= 0.7 , shuffle= True , random_state= 123 , stratify=df[ "labels" ] ) valid_df, test_df = train_test_split( tmp_df, train_size= 0.5 , shuffle= True , random_state= 123 , stratify=tmp_df[ "labels" ], ) ããŒã¿ã®æ¡åŒµ 深局åŠç¿ã©ã€ãã©ãªã§ãã Keras ã® ImageDataGenerator ã䜿ã£ãŠããŒã¿ãæ¡åŒµããŸãã gen = ImageDataGenerator( horizontal_flip= True , rotation_range= 20 , width_shift_range= 0.2 , height_shift_range= 0.2 , zoom_range= 0.2 , ) aug_img_count = 0 required = 100 # äœæããæ¡åŒµç»åã®ææ° aug_gen = gen.flow_from_dataframe( df, x_col= "filepaths" , y_col= None , target_size=img_size, class_mode= None , batch_size= 1 , shuffle= False , save_to_dir=target_dir, # `target_dir`ã§æå®ãããã£ã¬ã¯ããªã«äœæãããŸãã save_prefix= "aug-" , color_mode= "rgb" , save_format= "jpg" , ) while aug_img_count < required: images = next (aug_gen) aug_img_count += len (images) ããŒã¹ã¢ãã«ã®èªã¿èŸŒã¿ åºåå±€ä»è¿ãé€ããã¢ãã«ãèªã¿èŸŒã¿ïŒ include_top=False ïŒã2ã¯ã©ã¹ã®åé¡çµæãåºåããããã«çœ®ãæããŸããä»åã¯Efficientnet B3ã®ã¢ãã«ãå©çšããŠããŸãã base_model.trainable = True ã«ãããšãã¡ã€ã³ãã¥ãŒãã³ã°ã False ã«ãããšè»¢ç§»åŠç¿ãšãªããŸãã base_model = tf.keras.applications.efficientnet.EfficientNetB3( include_top= False , weights= "imagenet" , input_shape=img_shape, pooling= "max" ) base_model.trainable = True x = base_model.output x = Dropout(rate= 0.4 , seed= 1 )(x) output = Dense(class_count, activation= "softmax" )(x) model = Model(inputs=base_model.input, outputs=output) model.compile( Adamax(learning_rate=lr), loss= "categorical_crossentropy" , metrics=[ "accuracy" ] ) åŠç¿ã®å®è¡ åŒæ°ã«ãåŠç¿çšããŒã¿ïŒ train_gen ïŒãšæ€èšŒçšããŒã¿ïŒ valid_gen ïŒãæå®ã㊠model.fit() ãå®è¡ãããšåŠç¿ãã¹ã¿ãŒãããŸãã model.fit( x=train_gen, epochs=epochs, verbose= 1 , callbacks=callbacks, validation_data=valid_gen, validation_steps= None , shuffle= False , initial_epoch= 0 , ) model.save(model_save_path) ã³ã³ãœãŒã«ã®åºåãèŠãŠãããšã accuracy ã val_accuracy ã®å€ãè¯ããªã£ãŠããã®ãåãããŸãããšãããããåŠç¿ã¯ããŸãé²ãã§ããããã§ãã Epoch 1/40 20/20 [==============================] - ETA: 0s - loss: 8.0606 - accuracy: 0.7800 validation loss of 9.0880 is 0.0000 % below lowest loss, saving weights from epoch 1 as best weights 20/20 [==============================] - 76s 3s/step - loss: 8.0606 - accuracy: 0.7800 - val_loss: 9.0880 - val_accuracy: 0.7000 Epoch 2/40 20/20 [==============================] - ETA: 0s - loss: 6.8788 - accuracy: 0.9050 validation loss of 7.7559 is 14.6582 % below lowest loss, saving weights from epoch 2 as best weights 20/20 [==============================] - 34s 2s/step - loss: 6.8788 - accuracy: 0.9050 - val_loss: 7.7559 - val_accuracy: 0.7000 Epoch 3/40 20/20 [==============================] - ETA: 0s - loss: 6.2107 - accuracy: 0.9225 validation loss of 6.1968 is 20.1025 % below lowest loss, saving weights from epoch 3 as best weights 20/20 [==============================] - 34s 2s/step - loss: 6.2107 - accuracy: 0.9225 - val_loss: 6.1968 - val_accuracy: 0.9167 Epoch 4/40 20/20 [==============================] - ETA: 0s - loss: 5.6444 - accuracy: 0.9350 validation loss of 5.6057 is 9.5382 % below lowest loss, saving weights from epoch 4 as best weights 20/20 [==============================] - 35s 2s/step - loss: 5.6444 - accuracy: 0.9350 - val_loss: 5.6057 - val_accuracy: 0.9500 Epoch 5/40 20/20 [==============================] - ETA: 0s - loss: 5.1150 - accuracy: 0.9500 validation loss of 4.9568 is 11.5753 % below lowest loss, saving weights from epoch 5 as best weights 20/20 [==============================] - 37s 2s/step - loss: 5.1150 - accuracy: 0.9500 - val_loss: 4.9568 - val_accuracy: 0.9833 Epoch 6/40 20/20 [==============================] - ETA: 0s - loss: 4.6689 - accuracy: 0.9675 validation loss of 4.5310 is 8.5913 % below lowest loss, saving weights from epoch 6 as best weights 20/20 [==============================] - 34s 2s/step - loss: 4.6689 - accuracy: 0.9675 - val_loss: 4.5310 - val_accuracy: 0.9833 Epoch 7/40 20/20 [==============================] - ETA: 0s - loss: 4.2913 - accuracy: 0.9900 validation loss of 4.2057 is 7.1782 % below lowest loss, saving weights from epoch 7 as best weights 20/20 [==============================] - 34s 2s/step - loss: 4.2913 - accuracy: 0.9900 - val_loss: 4.2057 - val_accuracy: 0.9500 Epoch 8/40 20/20 [==============================] - ETA: 0s - loss: 3.9923 - accuracy: 0.9750 validation loss of 3.9645 is 5.7369 % below lowest loss, saving weights from epoch 8 as best weights 20/20 [==============================] - 34s 2s/step - loss: 3.9923 - accuracy: 0.9750 - val_loss: 3.9645 - val_accuracy: 0.9500 Epoch 9/40 20/20 [==============================] - ETA: 0s - loss: 3.7193 - accuracy: 0.9725 validation loss of 3.6385 is 8.2214 % below lowest loss, saving weights from epoch 9 as best weights ... æšè«åŠçã®å®è¡ ãã¹ãçšã®ããŒã¿ã model.predict() ã®åŒæ°ã«æž¡ãããšã§æšè«ãè¡ãããšãã§ããŸãã æšè«ã®çµæã¯ã [0.00226656, 0.99773353] ãšããããã«ããã®ç»åãããããã®ã¯ã©ã¹ã§ãã確çã®ãããªãã®ã衚ããŠãããããæ°å€ãæã倧ããã¯ã©ã¹ãåé¡çµæãšããŠäœ¿ããŸãã image_generator = ImageDataGenerator().flow_from_dataframe( test_df, x_col= "filepaths" , y_col= "labels" , target_size=image_size, class_mode= "categorical" , color_mode= "rgb" , shuffle= False , batch_size=batch_size, ) results = [] predicts = model.predict(image_generator, verbose= 1 ) for i, p in enumerate (predicts): file = image_generator.filenames[i] label_index = image_generator.labels[i] predicted_index = np.argmax(p) results.append(( file , predicted_index, label_index, p)) print (results) 粟床ã®è©äŸ¡ æåã«çšæãã15%ã®ãã¹ãçšç»åïŒ61æïŒã«å¯ŸããŠè©äŸ¡ããŠã¿ãçµæã以äžã®éãã§ãã label precision recall f1-score support 0 1.0000 0.8571 0.9231 21 1 0.9302 1.0000 0.9639 40 ããã£ãšãã£ãå²ã«ç°¡åã«ç²ŸåºŠãåºãŠãããªãé©ããŸããã AIã¢ãã«äœæã¯ç°¡åã§ã¯ãªãã£ã ãããïŒ å¥éãå¥ã®äººãçšæãããã¹ãçšã®ç»åã«å¯ŸããŠæšè«ãå®è¡ããŠè©äŸ¡ãããšãprecision 0.5, recall 0.8ïŒïŒãšããæ®å¿µãªçµæã§ãããã»ãšãã©ã©ã³ãã ã§0, 1éžæããã®ãšå€ãããªãïŒïŒ å
šãã®æªç¥ã®ç»åã«å¯ŸããŠç²ŸåºŠãåºãããšã®é£ãããããããŸãããã ããããç§ãéããåŠç¿çšããŒã¿ã»ããå
šäœã«å¯ŸããŠããªãåããããã®ã§ã¯ãªãããšããèå¯ãããŠããã®ã§ãããå®éã®ã¢ããªã±ãŒã·ã§ã³ãšããŠäœ¿ããã¬ãã«ã«ããã«ã¯ããå°ã詊è¡é¯èª€ãå¿
èŠããã§ãã 粟床ãåºãããã«ã¯ããå°ãå匷ãå¿
èŠããã§ãããåé¡ç³»ã®åŠç¿æžã¿ã¢ãã«ã®äœæïœæšè«åŠçã®å®è£
ãŸã§ãäžéãçµéšããããšãã§ããŸããïŒ Safieã®ãµãŒãã¹ãå©çšãããšç»ååéã®éšåã¯ããªã楜ãªã®ã§ããã®ç¹ã¯å€§ããªã¡ãªãããããããã§ãããããŸã ãŸã æéã§ããããšã«ã¯å€ãããªãã®ã§ãã©ãã«ä»ãã®CSVã®ãããªåœ¢ã§ããçšåºŠãŸãšãŸã£ãåäœã§ç»åããŠã³ããŒãã§ããããã«ãªããšãªãè¯ãããã§ãããã®èŸºãã¯ããã³ãããŒã ãšé£æºããŠå©äŸ¿æ§ãåäžãããŠãããããšæããŸãã æšè«ã®ç²ŸåºŠãäžããŠãç¡äºã«ã¢ããªã±ãŒã·ã§ã³ã宿ããŸããããŸããã®èšäºã§çµæãå
±æããããšæããŸãïŒ