Create MLでガンダムのMS画像(連邦、ジオン区分け)を学習したCore ML Modelをもとに、画像をどちらの軍所属機体かを判定してフォルダ分けするAppleScriptです。
機械学習(深層学習)データをもとに判定を行って対話型処理ではなくバッチ処理的なフローでデータを「なんとなく」推論して区分けを行うサンプルです。非力なIOT機器が機械学習データをもとに推論処理できるぐらいなので、機械学習データを使ってAppleScriptで推論処理できないはずがありません。推論処理時間もさほどかからず、機械学習データを一括処理的なワークフローの中で利用することが可能です。
例によって「戦場の絆」の公式ページから落としてきたMSの画像を連邦軍とジオン軍、および両軍の鹵獲機体の4フォルダに分けてCreate MLで学習させてみました。
実行するとMS画像が入っているフォルダ(各自ご用意ください)と、連邦軍機体(efsf)およびジオン軍機体(zeon)を仕分けるフォルダを聞かれます。あとはプログラム側がなんとなく推論して仕分けを行います。
前回掲載のモデルもそうですが、きちんと学習後にテストデータで評価を行っています。だいたい8割ぐらいのヒット率ではあるものの、特定の画像については間違えることがあらかじめわかっています。
実行にあたっては、macOS 10.14上でCoreML Modelを組み込んだフレームワーク「msDetector.framework」をインストールして、本ScriptをScript Debugger上で動作させてください。
–> Download msDetector.framework (To ~/Library/Frameworks/)
–> Download msDetector (Code Signed executable AppleScript applet with Framework and libraries in its bundle)
Core MLで判定時に詳細なデータを出力させ、その可能性が0.8を上回るものだけを処理するようにしてみました。ただ、全力で間違った判定を行うケースもあるため、単なる「気休め」です。
テストデータでは、連邦軍側が72機体中間違いが9個(正解87.5%)、ジオン軍側が57機体中間違いが3個(正解95%)。鹵獲機体のデータを排除すると、もう少しいい値が出るかもしれません。ただ、学習させたデータとほぼ同じデータ(数が少ない)で再度判定を行っているだけなので、このぐらいの確度が出ないと逆に困ります。
また、確度が高くないものは排除するように処理したので、確度が低い機体の画像バリエーションを増やして学習画像数を足すと正解の確率が上がるのではないか、などと「なんとなく」考えています。
Create MLでは、もっときちんとした表データやタグ付き自然言語テキストデータ(日本語だと形態素への区分けはあらかじめやっておく必要アリ?)の学習が行えるようなので(自然言語テキストの学習例は英語のものしか見かけないけれども)いろいろやっておきたいところです。
今回は「アニメとかゲームに出てくるMSの画像」の判定という、実用性をまったく考えないで行なってみたものですが、自分の身の回りで有用性の高そうな処理といえば……アイコンやプレゼン用資料の作成画像を学習させておおまかなジャンル分けをしておき(メール系のアイコンとか)、未知のアイコン素材をダウンロードしてきたら機械学習データをもとに自動でフォルダ分けするとかでしょうか。
さすがにアイコン画像だとデフォルメされすぎていて、MicrosoftのAzure Computer Vision APIとかでも画像認識してくれなさそうなので。
AppleScript名:機械学習で学習したMSの画像の連邦軍、ジオン軍判定してフォルダ分け v2.scpt |
— – Created by: Takaaki Naganoya – Created on: 2018/11/20 — – Copyright © 2018 Piyomaru Software, All Rights Reserved — use AppleScript version "2.7" — Mojave (10.14) or later & Script Debugger use framework "Foundation" use framework "AppKit" use framework "msDetector" use scripting additions property |NSURL| : a reference to current application’s |NSURL| property NSArray : a reference to current application’s NSArray property NSString : a reference to current application’s NSString property NSPredicate : a reference to current application’s NSPredicate property NSFileManager : a reference to current application’s NSFileManager property NSMutableArray : a reference to current application’s NSMutableArray property NSSortDescriptor : a reference to current application’s NSSortDescriptor property NSURLTypeIdentifierKey : a reference to current application’s NSURLTypeIdentifierKey set aFol to POSIX path of (choose folder with prompt "Choose MS Image folder to go!") set efsfFol to POSIX path of (choose folder with prompt "EFSF folder") set zeonFol to POSIX path of (choose folder with prompt "Zeon folder") –指定フォルダ以下のファイルをすべて取得(再帰ですべて取得) set aSel to retFullpathsWithinAFolderWithRecursive(aFol) of me –取得したファイルリストを指定UTIでフィルタリング set filRes2 to filterPOSIXpathListByUTI(aSel, "public.image") of me –機械学習モデルを用いて区分けを行い、任意の画像の連邦軍(efsf)、ジオン軍(zeon)へのフォルダ分け(コピー) set msClassifier to current application’s msClassify’s alloc()’s init() repeat with i in filRes2 set aFile to contents of i set aImage to (current application’s NSImage’s alloc()’s initWithContentsOfFile:aFile) set resDict to (msClassifier’s ImageClassifierWithImageAndRetDict:aImage) –returns NSDictionary –Dictinaryのvalueをソートして、最も値の大きいvalueのkeyを求める set sortedArray to (current application’s NSMutableArray’s arrayWithArray:(resDict’s allValues())) (sortedArray’s sortUsingSelector:"compare:") set aRes to (last item of (sortedArray as list)) –末尾のアイテムが一番値の大きい数値 set resKey to first item of ((resDict’s allKeysForObject:aRes) as list) –最大値をもとにKeyを求める –可能性の高いものだけ処理してみる(自信たっぷりに全力で間違えることもけっこうある) if (aRes as real) > 0.8 then if resKey begins with "efsf" then –efsf & efsf_stolen set aRes to (my copyFileAt:aFile toFolder:efsfFol) else –zeon & zeon_stolen set aRes to (my copyFileAt:aFile toFolder:zeonFol) end if end if end repeat –POSIX path listから指定UTIに含まれるものをPOSIX pathのリストで返す on filterPOSIXpathListByUTI(aList, targUTI) set newList to {} repeat with i in aList set j to contents of i set tmpUTI to my retUTIfromPath(j) set utiRes to my filterUTIList({tmpUTI}, targUTI) if utiRes is not equal to {} then set the end of newList to j end if end repeat return newList end filterPOSIXpathListByUTI –指定のPOSIX pathのファイルのUTIを求める on retUTIfromPath(aPOSIXPath) set aURL to |NSURL|’s fileURLWithPath:aPOSIXPath set {theResult, theValue} to aURL’s getResourceValue:(reference) forKey:NSURLTypeIdentifierKey |error|:(missing value) if theResult = true then return theValue as string else return theResult end if end retUTIfromPath –UTIリストが指定UTIに含まれているかどうか演算を行う on filterUTIList(aUTIList, aUTIstr) set anArray to NSArray’s arrayWithArray:aUTIList set aPred to NSPredicate’s predicateWithFormat_("SELF UTI-CONFORMS-TO %@", aUTIstr) set bRes to (anArray’s filteredArrayUsingPredicate:aPred) as list return bRes end filterUTIList –指定フォルダ以下のすべてのファイルを再帰で取得 on retFullpathsWithinAFolderWithRecursive(aFol) set anArray to NSMutableArray’s array() set aPath to NSString’s stringWithString:aFol set dirEnum to NSFileManager’s defaultManager()’s enumeratorAtPath:aPath repeat set aName to (dirEnum’s nextObject()) if aName = missing value then exit repeat set aFullPath to aPath’s stringByAppendingPathComponent:aName anArray’s addObject:(aFullPath as string) end repeat return anArray as list end retFullpathsWithinAFolderWithRecursive on copyFileAt:POSIXPath toFolder:folderPath set POSIXPath to NSString’s stringWithString:POSIXPath set folderPOSIXPath to NSString’s stringWithString:folderPath set theName to POSIXPath’s lastPathComponent() set newPath to folderPOSIXPath’s stringByAppendingPathComponent:theName set fileManager to NSFileManager’s defaultManager() set theResult to fileManager’s copyItemAtPath:POSIXPath toPath:newPath |error|:(missing value) return (theResult as integer = 1) end copyFileAt:toFolder: |
ぴよまるソフトウェアが選ぶ、2018年に書いた「価値あるScript」 – AppleScriptの穴 says:
[…] ・機械学習で学習したMSの画像を連邦・ジオン軍判定してフォルダ分け v2 強化学習によって作成したモデルをもとにバッチ処理を行うというのは、1つの目標として据えていたので、そ […]