Deep Learningに関し、Toyレベルのサンプルはいろいろオープンにされていて勉強できる。また、Academic Paperに、記録狙いの巨大なNeuralNetworkが書いてある。しかし、それらの間にある、ある程度、実用レベルを狙っているが、さほど複雑でない、NeuralNetworkの情報は、あまり公開されていません。
そこで、自分がやったことに関し、似たようなことをやろうとしている方に役立つかもしれないので、残しておきます。
以下、目周辺の画像のPixelに関し、まぶたの間の領域とそれ以外を2値分類する(セグメントする)、2値クラス分類モデルです。
ここの画像は、テスト入力画像と、Sigmoid出力[0..1]を、gray scaleの[0..255]に変換したMaskとを、bitwise_orしたサンプルです。
PyTorchを使っています。データローダーと、Training/testコードは、PyTorchのサイトのTutorialのもろもろのサンプルコードの微改造なので、省略します。
入力データは、
・縦48x横78pixel画像が、84K個。
・正解領域を1としそれ以外を0とするような、画像と同じサイズのマスク。
・90:10でTrainig:Test区分けしています。
データソースは、商用利用を許諾された画像を集め、Flip、明度変更、Crop、Rotationなどで、Data Argumentionしています。
正解Tagは、DlibのLandmark Tagをもとにして、輪郭をとって内側を塗りつぶし、多少のモルホロジー変換を施し作りましたが、Dlibは白人以外に弱い、眼鏡などがあると間違えやすい、などの問題があり、手修正が大変でした。
ネットワークモデルは、Sonyの小林さんという方がYouTubeに投稿しているSegmentationのTutorial動画を参考にしました。Convolutionを4層重ね、Sigmoidするだけですが、Dilationという仕組みでグローバルなコンテキストを認識するようにした。
Training結果のConfusion Matrixは数値化していませんが、どのテスト結果を見ても目領域を外していません。つまり、目領域ではないのに目領域であると判定された、False-Positiveはほとんどない。
たったこれだけのコードで、ほぼ完ぺきに目領域をセグメントできる。
class EyeMaskNet(nn.Module):
def __init__(self):
super(EyeMaskNet, self).__init__()
self.conv1 = nn.Conv2d(1, 16, (3,3), dilation=(1,1), padding=(1,1))
nn.init.kaiming_normal_(self.conv1.weight)
self.bn1 = nn.BatchNorm2d(16)
self.conv2 = nn.Conv2d(16, 16, (3,3), dilation=(2,2), padding=(2,2)) #dilate=2 -> kernel=5x5
nn.init.kaiming_normal_(self.conv2.weight)
self.bn2 = nn.BatchNorm2d(16)
self.conv3 = nn.Conv2d(16, 16, (3,3), dilation=(4,4), padding=(4,4)) #dilate=4 -> kernel=9x9
nn.init.kaiming_normal_(self.conv3.weight)
self.bn3 = nn.BatchNorm2d(16)
self.conv4 = nn.Conv2d(16, 16, (3,3), dilation=(8,8), padding=(8,8)) #dilate=8 -> kernel=17x17
nn.init.kaiming_normal_(self.conv4.weight)
self.bn4 = nn.BatchNorm2d(16)
self.convf = nn.Conv2d(16, 1, (3,3), dilation=(1,1), padding=(1,1))
nn.init.kaiming_normal_(self.convf.weight)
def forward(self, x):
x = x.float()
x = F.relu(self.bn1(self.conv1(x)))
x = F.relu(self.bn2(self.conv2(x)))
x = F.relu(self.bn3(self.conv3(x)))
x = F.relu(self.bn4(self.conv4(x)))
x = F.sigmoid(self.convf(x))
return x