Golang : DeepLearningを用いた画像分類

この記事は約11分で読めます。

今回は Golang でDeep Learning を試してみます。

 

以前

Go言語 :簡単な AI を作ってみよう~
今回はGo言語入門~簡単なAI( ニューラルネットワーク )~を作ってみようです。 最初の AI は煽りすぎました...

 

この記事で、ニューラルネットワーク(脳のシナプスを模したモデル)を用いて、

XORの学習をしました!

本記事では、少し応用して、画像分類をしてみようと思います!

画像分類の考え方

  1. 画像を一次元配列化(0~1)(Golangだと、[]float64)にする
  2. 画像にラベルをつける(one hot)
  3. Deep Learning のモデルに、インプット(画像を一次元のデータ化->以下画像のバイナリ)と期待するアウトプット(ラベル)が書かれた学習データを与え、学習させる
  4. 学習させたモデルに、画像の一次元配列を入れると、モデル内で推論して、アウトプットを出す

Deep Learning(深層学習)とは

前回の

[Go]Go言語入門~簡単なAIを作ってみよう~

では、

  • 入力
  • 中間
  • 出力

でした。

DeepLearningでは

この中間層を多層構造化しています。

今回は、DeepLearningを用いていきます!

それではいきましょう!

 

実践

前回の課題

[Go]Go言語入門~簡単なAIを作ってみよう~

ここで実装したXORは

過学習という状態でした。

また、活性化関数がシグモイドしか使えませんでした。

今回は、画像分類なので、ソフトマックス関数が使えるライブラリを使っていきます。

 

FFXの

ティーダ

ユウナ

ルールーの画像分類をしていきます!

利用するライブラリ

今回利用するライブラリは

GitHub - patrikeh/go-deep: Artificial Neural Network

です!

前回に比べ、細かい設定ができます。

 

ただ、このライブラリは画像ファイルを学習データに変換したり、画像のラベルを作成したりする機能は搭載されてません。

今回は、僕の方で、ライブラリを作成したので、それを用います。

(高精度に画像認識するには、特徴量の抽出などが必要です)

GitHub - ryomak/go-deep-util: go-deep-util is utility for github.com/patrikeh/go-deep

 

ディレクトリ

├── dataset
│   ├── lulu(ルールーの画像を集める->Label{1,0,0})
│   ├── tida(ティーダの画像を集める->Label{0,1,0})
│   └── yuna(ユウナの画像を集める->Label{0,0,1})
├── go.mod
├── go.sum
├── main.go
└── input.jpg

datasetの下にある、フォルダがそれぞれ学習させる画像を集めたものです。僕が作ったライブラリでは、上から順に自動的にラベルが振られます。

学習させる画像を用意する

下の画像のように、とりあえずたくさん画像を入れてください。

コード

 

package main

import (
	"fmt"
	"math/rand"
	"os"
	"time"

	"github.com/briandowns/spinner"
	deep "github.com/patrikeh/go-deep"
	"github.com/patrikeh/go-deep/training"
	util "github.com/ryomak/go-deep-util"
	iclass "github.com/ryomak/go-deep-util/iclassifier"
)

func main() {

	loading := spinner.New(spinner.CharSets[9], 100*time.Millisecond)

	labels := []string{
		"tida",
		"yuna",
		"lulu",
	}

	var i util.IBrainUtil

	i = iclass.Init(
		labels,
		"dataset",
		30,
		30,
	)
	rand.Seed(time.Now().UnixNano())

	data, err := i.MakePattern()
	if err != nil {
		panic(err)
	}

	if len(data) == 0 {
		fmt.Println("no data")
		os.Exit(1)
	}

	//shuffle
	ex := training.Examples(util.DatsetToDataSets(data))
	ex.Shuffle()

	neural := deep.NewNeural(&deep.Config{
		Inputs:     len(data[0].Input),
		Layout:     append([]int{1000, 100}, len(data[0].Response)),
		Activation: deep.ActivationSoftmax,
		Mode:       deep.ModeMultiClass,
		Weight:     deep.NewNormal(1, 0),
		Bias:       true,
	})

	fmt.Printf("train start[testcase:%d] ...\n", len(data))
	loading.Start()
	trainer := training.NewBatchTrainer(training.NewAdam(0.001, 0, 0, 0), 40, len(ex)/2, 12)
	training, heldout := ex.Split(0.8)
	trainer.Train(neural, training, heldout, 60)
	loading.Stop()

	inputFile := "input.jpg"
	gazou, _ := i.Decode(inputFile)
	out, _ := i.Encode(neural.Predict(gazou))

	fmt.Printf("[Result]\n%s is maybe : %v \n", inputFile, out)

	doTest(neural, ex, i)
}

func doTest(neural *deep.Neural, ex training.Examples, i util.IBrainUtil) {

	fmt.Println("Test start with learned Model")
	sum := float64(len(ex))
	correct := 0.0

	for _, p := range ex {
		actual, _ := i.Encode(neural.Predict(p.Input))
		except, _ := i.Encode(p.Response)
		if actual == except {
			correct++
		} else {
			fmt.Printf("miss:except: %s,but actual: %s\n", except, actual)
		}
	}
	fmt.Printf("[Test Result]\ncorrect:%v, sum:%v  %0.1f%\n", correct, sum, 100*correct/sum)
}

コード解説

まず、僕が作ったライブラリを用いて、画像ファイルから学習データを作成していきます。

labels := []string{ "tida", "yuna", "lulu", } 	
        i := iclass.Init(
		labels,
		"dataset",
		30,
		30,
	)
	data, err := i.MakePattern()
	if err != nil {
		panic(err)
	}
	ex := training.Examples(util.DatsetToDataSets(data))
	ex.Shuffle()
  1. iclass.Init()の第一引数である、stringのスライスには、学習データに使う画像フォルダ名を入れます。
  2. iclass.Init()の第二引数では、その学習データフォルダがあるディレクトリまでのパスを入れます。今回はdatasetフォルダの中にある画像フォルダを選択しています。
  3. iclass.Init()の第三、四引数では、学習させる画像の縦と横の長さを指定します。大きいほど詳細にバイナリ化しますが、データが大きくなりすぎて、学習に時間がかかるので、30✖️30にしています。
  4. i.MakePattern()で、学習データを作成します。ここでは、既にラベリングもされています。
  5. exでは、go-deepライブラリで使える学習データの構造に変換し、学習データをシャッフルしています。

これで下準備完成です。

次から、go-deepの設定です。

 

neural := deep.NewNeural(&deep.Config{
		Inputs:     len(data[0].Input),
		Layout:     append([]int{1000, 100}, len(data[0].Response)),
		Activation: deep.ActivationSoftmax,
		Mode:       deep.ModeMultiClass,
		Weight:     deep.NewNormal(1, 0),
		Bias:       true,
	})
 	
        trainer := training.NewBatchTrainer(training.NewAdam(0.001, 0, 0, 0), 40, len(ex)/2, 12)
	training, heldout := ex.Split(0.8)
	trainer.Train(neural, training, heldout, 100)
  1. deep.NewNeural()で、学習モデルの設定をします。設定は様々なので、公式サイトを見ていただきたいのですが、Layoutに注目してください。Layoutで中間層から出力層を設定しています。
  2. training.NewBatchTrainer()では、学習方法を設定しています。BatchTrainerは並行処理をしてくれるので、比較的早くトレーニングしてくれます。
  3. trainer.Train()で、学習させています。ここが一番処理に時間がかかります。第四引数で、何回学習させるかを指定しています。今回は100回学習させています。

 

次に学習させたモデルに、分類させたい画像を入れて、推論してもらいます。

inputFile := "input.jpg"
	gazou, _ := i.Decode(inputFile)
	out, _ := i.Encode(neural.Predict(gazou))

	fmt.Printf("[Result]\n%s is maybe : %v \n", inputFile, out)
  1. iclass.Decode()でinput.jpegをバイナリ化しています。(MakePatternメソッドでも内部的に使っています)
  2. neural.Predict(gazou)で、推論します。({x,x,x}みたいにラベルの形でできます)
  3. 推論した結果をiclass.Encode()で、ラベルをフォルダ名に戻しています。(今回だと、yuna or tida or lulu)
  4. そこから下は、学習データに対して、どれだけ正解するかをテストするコードです。

 

実行結果

$go run main.go

2019/12/15 17:12:01 dataset/tida/.DS_Store  can't decode
2019/12/15 17:12:01 dataset/tida/13.00001534_01.jpg  can't decode
train start[testcase:58] ...
| Epochs          Elapsed           Loss (CE)       Accuracy
---             ---               ---             ---
40              1m21.267788784s   NaN             0.56
[Result]
input.jpg is maybe : yuna

Test start with learned Model
[miss]except:lulu ,but actual:yuna
[miss]except:tida ,but actual:yuna
[miss]except:yuna ,but actual:tida
[miss]except:yuna ,but actual:tida
[miss]except:lulu ,but actual:tida
[miss]except:yuna ,but actual:tida
[miss]except:yuna ,but actual:tida
[miss]except:tida ,but actual:yuna
[Test Result]
correct:50, sum:58  86.2%

まとめ

今回は、Golang でDeepLearningを用いた画像分類を行いました。

パラメータや、活性化関数を変えると、実行結果もかなり変わります。

今回は画像分類でしたが、画像生成についても作りました!

ぜひ使ってみてください。上手くいった方教えてください!

 

https://github.com/ryomak/deep-learning-go/blob/master/image/image_maker/imaker.go

 

それでは。

 

参考文献

Golang だけでやる機械学習と画像分類

mattnさんの画像を扱う方法をかなり参考にさせていただきました。

一次元配列化の仕組みを詳しく知りたい人は上の記事へ

タイトルとURLをコピーしました