gobrain という Golang だけで実装されたニューラルネットワークを見つけたので遊んでみました。
GitHub - goml/gobrain: Neural Networks written in go
https://github.com/goml/gobrain
作りもシンプルですし、扱い方も簡単なのでちょっとしたサンプルを書くのには向いてると思います。例えば FizzBuzz であればこんな感じ。
package main
import (
    "math/rand"
    "github.com/goml/gobrain"
)
type FizzBuzz []float64
func (fizzbuzz FizzBuzz) Type() int {
    for i := 0; i < len(fizzbuzz); i++ {
        if fizzbuzz[i] > 0.4 {
            return i
        }
    }
    panic("Sorry, I'm wrong")
}
func teacher(n int) []float64 {
    switch {
    case n%15 == 0:
        return []float64{1, 0, 0, 0}
    case n%3 == 0:
        return []float64{0, 1, 0, 0}
    case n%5 == 0:
        return []float64{0, 0, 1, 0}
    default:
        return []float64{0, 0, 0, 1}
    }
}
func bin(n int) []float64 {
    f := [8]float64{}
    for i := uint(0); i < 8; i++ {
        f[i] = float64((n >> i) & 1)
    }
    return f[:]
}
func main() {
    rand.Seed(0)
    // make patterns
    patterns := [][][]float64{}
    for i := 1; i <= 100; i++ {
        patterns = append(patterns, [][]float64{
            bin(i), teacher(i),
        })
    }
    ff := &gobrain.FeedForward{}
    // 8 inputs, 100 hidden nodes, 4 outputs
    ff.Init(8, 100, 4)
    // epochs: 1000
    // learning rate: 0.6
    // momentum factor: to 0.4
    ff.Train(patterns, 1000, 0.6, 0.4, false)
    for i := 1; i < 100; i++ {
        switch FizzBuzz(ff.Update(bin(i))).Type() {
        case 0:
            println("FizzBuzz")
        case 1:
            println("Fizz")
        case 2:
            println("Buzz")
        case 3:
            println(i)
        }
    }
}
今日はこの gobrain を使って画像分類を作ってみました。特徴抽出やノーマライズはやってないので実用的ではない事に注意下さい。
まず flickr 等から薔薇とユリと向日葵の画像を貰ってきて下さい。
薔薇
ユリ
刺青混じってませんか...
向日葵
各20毎程度で構いません。次に画像を読み込んで3チャネルに分割します。
func decodeImage(fname string) ([]float64, error) {
    f, err := os.Open(fname)
    if err != nil {
        return nil, err
    }
    defer f.Close()
    src, _, err := image.Decode(f)
    if err != nil {
        return nil, err
    }
    bounds := src.Bounds()
    w, h := bounds.Dx(), bounds.Dy()
    if w < h {
        w = h
    } else {
        h = w
    }
    bb := make([]float64, w*h*3)
    for y := 0; y < h; y++ {
        for x := 0; x < w; x++ {
            r, g, b, _ := src.At(x, y).RGBA()
            bb[y*w*3+x*3] = float64(r) / 255.0
            bb[y*w*3+x*3+1] = float64(g) / 255.0
            bb[y*w*3+x*3+2] = float64(b) / 255.0
        }
    }
    return bb, nil
}
これで画像データが1次元の float64 配列になりこれが入力となります。これに薔薇やユリや向日葵のラベルを紐づけるためにラベルの添え字番号を使い同じ様に float64 配列にする関数を作ります。
func bin(n int) []float64 {
    f := [8]float64{}
    for i := uint(0); i < 8; i++ {
        f[i] = float64((n >> i) & 1)
    }
    return f[:]
}
func dec(d []float64) int {
    n := 0
    for i, v := range d {
        if v > 0.9 {
            n += 1 << uint(i)
        }
    }
    return n
}
あとは gobrain を初期化して学習させれば推論器が出来上がるのですが
ff.Init(len(patterns[0][0]), 40, len(patterns[0][1]))
ff.Train(patterns, 1000, 0.6, 0.4, false)
gobrain は Pure Go という事もあり struct をそのまま JSON にエンコードしてやればこれがモデルファイルになる事に気付きました。
func loadModel() (*gobrain.FeedForward, []string, error) {
    f, err := os.Open("labels.txt")
    if err != nil {
        return nil, nil, err
    }
    defer f.Close()
    labels := []string{}
    scanner := bufio.NewScanner(f)
    for scanner.Scan() {
        labels = append(labels, scanner.Text())
    }
    if scanner.Err() != nil {
        return nil, nil, err
    }
    if len(labels) == 0 {
        return nil, nil, errors.New("No labels found")
    }
    f, err = os.Open("model.json")
    if err != nil {
        return nil, labels, nil
    }
    defer f.Close()
    ff := &gobrain.FeedForward{}
    err = json.NewDecoder(f).Decode(ff)
    if err != nil {
        return nil, labels, err
    }
    return ff, labels, nil
}
func makeModel(labels []string) (*gobrain.FeedForward, error) {
    ff := &gobrain.FeedForward{}
    patterns := [][][]float64{}
    for i, category := range labels {
        bset, err := loadImageSet(category)
        if err != nil {
            return nil, err
        }
        for _, b := range bset {
            patterns = append(patterns, [][]float64{b, bin(i)})
        }
    }
    if len(patterns) == 0 || len(patterns[0][0]) == 0 {
        return nil, errors.New("No images found")
    }
    ff.Init(len(patterns[0][0]), 40, len(patterns[0][1]))
    ff.Train(patterns, 1000, 0.6, 0.4, false)
    return ff, nil
}
func saveModel(ff *gobrain.FeedForward) error {
    f, err := os.Create("model.json")
    if err != nil {
        return err
    }
    defer f.Close()
    return json.NewEncoder(f).Encode(ff)
}
全体のソースは GitHub に置いてあります。
GitHub - mattn/flower-detect
https://github.com/mattn/flower-detect
実際に試してみます。
結果は「sunflower」。そうだよ向日葵だよ。
結果は「rose」。そうだよ薔薇だよ。
結果は「lilium」。そうだよユリだよ。
gobrain を JSON で出力してモデル扱いにするというこの方法を使えば、簡単な画像分類であればインストールが難しい TensorFlow を使わずともポータブルに実行出来ます。特に GPU を使う程ではないといった場合にも便利かなと思います。一応 smartcrop というパッケージを使って画像内で注目される部分で crop する様にしてありますが、いくらかの画像では失敗します。これは画像をノーマライズしていないのでしょうがないですね。学習には10分くらい掛かると思います。
尚 Golang で TensorFlow やりたい人は以前書いた記事の方を参照下さい。
Big Sky :: golang で tensorflow のススメ
tensorflow といえば Python と思っておられる方も多いのではないでしょうか。間違いではないのですが、これは初期に作られた Python 向けのバインディングに研究者達が多く食いついた結...
https://mattn.kaoriya.net/software/lang/go/20180825013735.htm







 
 
 

 
 
  
 
