Murayama blog.

プログラミング教育なブログ

Xavierの初期値 Heの初期値の考察

ゼロから作るDeep Learning 第6章を参考に、ニューラルネットワークの隠れ層のアクティベーション(活性化関数の出力)の分布を確認してみます。

次のプログラムは1000件のサンプル(1つのサンプルは100次元のベクトル)を、5層の隠れ層(ノードはすべて100)に流すものです。

import numpy as np
import matplotlib.pyplot as plt

def sigmoid(x):
    return 1 / (1 + np.exp(-x))

def tanh(x):
    return np.tanh(x)

def relu(x):
    return np.maximum(0, x)

def show_activation(activation, weights, ylim=(0, 50000)):

    x = np.random.randn(1000, 100)
    node = 100
    hidden = 5
    activations = {}
    for i in range(hidden):
        if i != 0:
            x = activations[i - 1]
      
        w = weights(node)
        z = np.dot(x, w)
        a = activation(z)
        activations[i] = a
      
    plt.figure(figsize=(18, 4))
    for i, a in activations.items():
        plt.subplot(1, len(activations), i + 1)
        plt.title(str(i + 1) + "-layer")
        plt.hist(a.flatten(), 30, range=(0,1))
        plt.ylim(ylim)
    plt.show()

show_activation(sigmoid, lambda n: np.random.randn(n, n) * 1)
# show_activation(sigmoid, lambda n: np.random.randn(n, n) * 0.01)
# show_activation(sigmoid, lambda n: np.random.randn(n, n) *  np.sqrt(1.0 / n), (0, 10000)) # Xavier

# show_activation(relu, lambda n: np.random.randn(n, n) * 0.01, (0, 7000))
# show_activation(relu, lambda n: np.random.randn(n, n) * np.sqrt(1.0 / n), (0, 7000)) # Xavier
# show_activation(relu, lambda n: np.random.randn(n, n) * np.sqrt(2.0 / n), (0, 7000)) # He

このプログラムでは重みのスケールを標準偏差1のガウス分布としています。

show_activation(sigmoid, lambda n: np.random.randn(n, n) * 1)

以降、重みを変更するとアクティベーションにどのような変化を及ぼすか確認していきます。

標準偏差1の場合

標準偏差1の実行結果は次のようになります。

f:id:yamasahi:20171125231255p:plain

アクティベーションが0と1に偏っているのがわかります。sigmoid関数の出力が0に近くにつれて(あるいは1に近くづにつれて)、その微分の値は0に近づきます。そのため、0と1に偏った分布では逆伝搬での勾配の値が小さくなってしまいます。このような現象は「勾配消失問題(gradient vanishing)」と呼ばれます。

標準偏差0.01の場合

次は重みのスケールを標準偏差0.01のガウス分布としています。

show_activation(sigmoid, lambda n: np.random.randn(n, n) * 0.01)

結果は次のようになります。

f:id:yamasahi:20171125231322p:plain

0.5付近に集中するようになりました。勾配消失問題は解消できましたが、アクティベーションに偏りがあるということは表現力が乏しいということです。複数のニューロンが同じような出力をするのであれば、ニューロンが複数存在する意味が失われてしまいます。「表現力の制限」が問題になってしまいます。

Xavierの初期値の場合

次にXavier Glorotの初期値を試してみます。これは前層のノード数が n の場合 1/sqrt(n) を標準偏差とした分布を使うというものです。

Kerasの場合はglorot_uniform、florot_normalのような初期値が定義されています。

show_activation(sigmoid, lambda n: np.random.randn(n, n) *  np.sqrt(1.0 / n), (0, 10000)) # Xavier

結果は次のようになります。

f:id:yamasahi:20171125231336p:plain

これまでの結果に比べてばらつきのある結果を得ることができました。「勾配消失問題」や「表現力の制限」といった問題を上手く回避できているのがわかります。

ReLU関数の場合

ここまでsigmoid関数のアクティベーションを見てきました。ReLU関数の場合はどうでしょうか。

標準偏差0.01の場合

show_activation(relu, lambda n: np.random.randn(n, n) * 0.01, (0, 7000))

f:id:yamasahi:20171125231354p:plain

Xavierの初期値の場合

show_activation(relu, lambda n: np.random.randn(n, n) * np.sqrt(1.0 / n), (0, 7000)) # Xavier

f:id:yamasahi:20171125231403p:plain

ReLU関数の場合、Xavierの初期値を使ったとしても、層が深くなるにつれて偏りが大きくなります。そこでReLU関数の場合はHeの初期値を使うことでこのようなケースに対処できます。Heの初期値は sqrt(2.0 / n)を標準偏差とするものです。ReLU関数の場合、負の領域がすべて0になるため、ばらつきにより広がりを持たせるために、2倍にすると考えます。

Heの初期値の場合

show_activation(relu, lambda n: np.random.randn(n, n) * np.sqrt(2.0 / n), (0, 7000)) # He

実行結果を見ていましょう。

f:id:yamasahi:20171125231413p:plain

Heの初期値を使えば偏りのないアクティベーションを確認することができました。

Kerasで画像認識 - MNIST編 - ReLU関数

KerasのMNISTのサンプルプログラムについて、活性化関数をsigmoid関数からReLU関数に変更してみましょう。

from keras.models import Sequential
from keras.layers import Dense, Activation
from keras.utils import to_categorical
from keras.datasets import mnist

(x_train, y_train), (x_test, y_test) = mnist.load_data()

# 28x28 => 784
x_train = x_train.reshape(60000, 784)
x_test = x_test.reshape(10000, 784)

# one-hot ex: 3 => [0,0,0,1,0,0,0,0,0,0]
y_train = to_categorical(y_train, 10)
y_test = to_categorical(y_test, 10)

model = Sequential()
model.add(Dense(50, input_dim=784))
model.add(Activation('relu'))
model.add(Dense(20))
model.add(Activation('relu'))
model.add(Dense(10))
model.add(Activation('softmax'))
model.compile(optimizer='sgd', loss='categorical_crossentropy', metrics=['accuracy'])

history = model.fit(x_train, y_train, batch_size=32, validation_data=(x_test, y_test))

プログラムを実行してみると、、学習が上手くいかないようです。

Train on 60000 samples, validate on 10000 samples
Epoch 1/10
60000/60000 [==============================] - 10s - loss: 14.5407 - acc: 0.0977 - val_loss: 14.5353 - val_acc: 0.0982
Epoch 2/10
60000/60000 [==============================] - 7s - loss: 14.5487 - acc: 0.0974 - val_loss: 14.5353 - val_acc: 0.0982
Epoch 3/10
60000/60000 [==============================] - 7s - loss: 14.5487 - acc: 0.0974 - val_loss: 14.5353 - val_acc: 0.0982
Epoch 4/10
60000/60000 [==============================] - 6s - loss: 14.5487 - acc: 0.0974 - val_loss: 14.5353 - val_acc: 0.0982
Epoch 5/10
60000/60000 [==============================] - 6s - loss: 14.5487 - acc: 0.0974 - val_loss: 14.5353 - val_acc: 0.0982
Epoch 6/10
60000/60000 [==============================] - 6s - loss: 14.5487 - acc: 0.0974 - val_loss: 14.5353 - val_acc: 0.0982
Epoch 7/10
60000/60000 [==============================] - 7s - loss: 14.5487 - acc: 0.0974 - val_loss: 14.5353 - val_acc: 0.0982
Epoch 8/10
60000/60000 [==============================] - 6s - loss: 14.5487 - acc: 0.0974 - val_loss: 14.5353 - val_acc: 0.0982
Epoch 9/10
60000/60000 [==============================] - 7s - loss: 14.5487 - acc: 0.0974 - val_loss: 14.5353 - val_acc: 0.0982
Epoch 10/10
60000/60000 [==============================] - 7s - loss: 14.5487 - acc: 0.0974 - val_loss: 14.5353 - val_acc: 0.0982

入力データの正規化

MNISTの画像ベクトルは0-255の値が格納されています。学習が上手く進むようにこの値を0-1の値に変換しておきます。

# 0-255 => 0-1
x_train = x_train.astype('float32')
x_test = x_test.astype('float32')
x_train /= 255
x_test /= 255

このような作業を正規化といいます。

プログラムを次のように実装して再度実行してみましょう。

from keras.models import Sequential
from keras.layers import Dense, Activation
from keras.utils import to_categorical
from keras.datasets import mnist

(x_train, y_train), (x_test, y_test) = mnist.load_data()

# 28x28 => 784
x_train = x_train.reshape(60000, 784)
x_test = x_test.reshape(10000, 784)

# 0-255 => 0-1
x_train = x_train.astype('float32')
x_test = x_test.astype('float32')
x_train /= 255
x_test /= 255

# one-hot ex: 3 => [0,0,0,1,0,0,0,0,0,0]
y_train = to_categorical(y_train, 10)
y_test = to_categorical(y_test, 10)

model = Sequential()
model.add(Dense(50, input_dim=784))
model.add(Activation('relu'))
model.add(Dense(20))
model.add(Activation('relu'))
model.add(Dense(10))
model.add(Activation('softmax'))
model.compile(optimizer='sgd', loss='categorical_crossentropy', metrics=['accuracy'])

history = model.fit(x_train, y_train, batch_size=32, validation_data=(x_test, y_test))

プログラムの実行結果は次のようになります。

Train on 60000 samples, validate on 10000 samples
Epoch 1/10
60000/60000 [==============================] - 6s - loss: 0.6895 - acc: 0.8073 - val_loss: 0.3421 - val_acc: 0.9029
Epoch 2/10
60000/60000 [==============================] - 6s - loss: 0.3159 - acc: 0.9085 - val_loss: 0.2759 - val_acc: 0.9218
Epoch 3/10
60000/60000 [==============================] - 8s - loss: 0.2626 - acc: 0.9243 - val_loss: 0.2375 - val_acc: 0.9306
Epoch 4/10
60000/60000 [==============================] - 14s - loss: 0.2282 - acc: 0.9338 - val_loss: 0.2081 - val_acc: 0.9388
Epoch 5/10
60000/60000 [==============================] - 8s - loss: 0.2027 - acc: 0.9413 - val_loss: 0.1904 - val_acc: 0.9437
Epoch 6/10
60000/60000 [==============================] - 7s - loss: 0.1833 - acc: 0.9469 - val_loss: 0.1774 - val_acc: 0.9468
Epoch 7/10
60000/60000 [==============================] - 6s - loss: 0.1682 - acc: 0.9520 - val_loss: 0.1669 - val_acc: 0.9516
Epoch 8/10
60000/60000 [==============================] - 7s - loss: 0.1560 - acc: 0.9541 - val_loss: 0.1575 - val_acc: 0.9531
Epoch 9/10
60000/60000 [==============================] - 6s - loss: 0.1452 - acc: 0.9578 - val_loss: 0.1476 - val_acc: 0.9548
Epoch 10/10
60000/60000 [==============================] - 6s - loss: 0.1360 - acc: 0.9606 - val_loss: 0.1412 - val_acc: 0.9563

今度は上手く学習できているようです。前回sigmoid関数で実行した場合は90%程度でしたので5%近く結果は向上したようです。

Heの初期値

KerasはDenseレイヤーの重みの初期化にglorot_uniform(Glorot(Xavier)の一様分布)を返します。sigmoid関数の場合はGlorotが良いようですが、ReLU関数を使う場合、He の正規分布を使うのが良いとされています。こちらも試してみましょう。

重みの初期化は次のように実装します。

from keras.initializers import he_normal

model.add(Dense(20, kernel_initializer=he_normal()))

利用可能な初期値はKerasのマニュアルページが参考になります。

https://keras.io/ja/initializers/

先ほどのプログラムを修正してみましょう。

from keras.models import Sequential
from keras.layers import Dense, Activation
from keras.utils import to_categorical
from keras.datasets import mnist
from keras.initializers import he_normal

(x_train, y_train), (x_test, y_test) = mnist.load_data()

# 28x28 => 784
x_train = x_train.reshape(60000, 784)
x_test = x_test.reshape(10000, 784)

# 0-255 => 0-1
x_train = x_train.astype('float32')
x_test = x_test.astype('float32')
x_train /= 255
x_test /= 255

# one-hot ex: 3 => [0,0,0,1,0,0,0,0,0,0]
y_train = to_categorical(y_train, 10)
y_test = to_categorical(y_test, 10)

model = Sequential()
model.add(Dense(50, input_dim=784, kernel_initializer=he_normal()))
model.add(Activation('relu'))
model.add(Dense(20, kernel_initializer=he_normal()))
model.add(Activation('relu'))
model.add(Dense(10, kernel_initializer=he_normal()))
model.add(Activation('softmax'))
model.compile(optimizer='sgd', loss='categorical_crossentropy', metrics=['accuracy'])

history2 = model.fit(x_train, y_train, batch_size=32, validation_data=(x_test, y_test))

プログラムの実行結果は次のようになります。

Train on 60000 samples, validate on 10000 samples
Epoch 1/10
60000/60000 [==============================] - 12s - loss: 0.7723 - acc: 0.7700 - val_loss: 0.3472 - val_acc: 0.8991
Epoch 2/10
60000/60000 [==============================] - 15s - loss: 0.3223 - acc: 0.9079 - val_loss: 0.2798 - val_acc: 0.9179
Epoch 3/10
60000/60000 [==============================] - 9s - loss: 0.2673 - acc: 0.9229 - val_loss: 0.2414 - val_acc: 0.9291
Epoch 4/10
60000/60000 [==============================] - 6s - loss: 0.2325 - acc: 0.9336 - val_loss: 0.2169 - val_acc: 0.9371
Epoch 5/10
60000/60000 [==============================] - 8s - loss: 0.2070 - acc: 0.9406 - val_loss: 0.1937 - val_acc: 0.9427
Epoch 6/10
60000/60000 [==============================] - 14s - loss: 0.1872 - acc: 0.9458 - val_loss: 0.1786 - val_acc: 0.9484
Epoch 7/10
60000/60000 [==============================] - 9s - loss: 0.1706 - acc: 0.9509 - val_loss: 0.1644 - val_acc: 0.9520
Epoch 8/10
60000/60000 [==============================] - 9s - loss: 0.1561 - acc: 0.9547 - val_loss: 0.1552 - val_acc: 0.9542
Epoch 9/10
60000/60000 [==============================] - 9s - loss: 0.1443 - acc: 0.9582 - val_loss: 0.1445 - val_acc: 0.9576
Epoch 10/10
60000/60000 [==============================] - 10s - loss: 0.1344 - acc: 0.9611 - val_loss: 0.1369 - val_acc: 0.9589

若干の改善はありませんが、大きな変化はみられませんでした。今回のMNISTデータだと効果がわかりにくいのかもしれません。重みの初期化についてはまた時間のあるときに考察してみようと思います。

Kerasで画像認識 - MNIST編

Kerasを使った画像認識のプログラムです。有名なMNISTデータ(手書き数字)を使ったものです。

MNIST handwritten digit database, Yann LeCun, Corinna Cortes and Chris Burges

from keras.models import Sequential
from keras.layers import Dense, Activation
from keras.utils import to_categorical
from keras.datasets import mnist

(x_train, y_train), (x_test, y_test) = mnist.load_data()

# 28x28 => 784
x_train = x_train.reshape(60000, 784)
x_test = x_test.reshape(10000, 784)

# one-hot ex: 3 => [0,0,0,1,0,0,0,0,0,0]
y_train = to_categorical(y_train, 10)
y_test = to_categorical(y_test, 10)

model = Sequential()
model.add(Dense(50, input_dim=784))
model.add(Activation('sigmoid'))
model.add(Dense(20))
model.add(Activation('sigmoid'))
model.add(Dense(10))
model.add(Activation('softmax'))
model.compile(optimizer='sgd', loss='categorical_crossentropy', metrics=['accuracy'])

history = model.fit(x_train, y_train, batch_size=32, validation_data=(x_test, y_test))

コードを書いて実行してみましょう。機械学習の開発環境にはJupyter Notebookがオススメです。

murayama.hatenablog.com

実行結果は次のようになります。テストデータで91%の正答率(val_acc)です。

Downloading data from https://s3.amazonaws.com/img-datasets/mnist.npz
10862592/11490434 [===========================>..] - ETA: 0sTrain on 60000 samples, validate on 10000 samples
Train on 60000 samples, validate on 10000 samples
Epoch 1/10
60000/60000 [==============================] - 6s - loss: 1.8023 - acc: 0.5735 - val_loss: 1.3870 - val_acc: 0.7524
Epoch 2/10
60000/60000 [==============================] - 5s - loss: 1.0939 - acc: 0.8008 - val_loss: 0.8456 - val_acc: 0.8457
Epoch 3/10
60000/60000 [==============================] - 5s - loss: 0.7209 - acc: 0.8618 - val_loss: 0.6233 - val_acc: 0.8767
Epoch 4/10
60000/60000 [==============================] - 5s - loss: 0.5610 - acc: 0.8814 - val_loss: 0.5006 - val_acc: 0.8945
Epoch 5/10
60000/60000 [==============================] - 5s - loss: 0.4770 - acc: 0.8892 - val_loss: 0.4278 - val_acc: 0.8998
Epoch 6/10
60000/60000 [==============================] - 6s - loss: 0.4267 - acc: 0.8960 - val_loss: 0.3993 - val_acc: 0.9026
Epoch 7/10
60000/60000 [==============================] - 8s - loss: 0.4097 - acc: 0.8970 - val_loss: 0.3959 - val_acc: 0.8984
Epoch 8/10
60000/60000 [==============================] - 6s - loss: 0.3875 - acc: 0.9016 - val_loss: 0.3856 - val_acc: 0.9044
Epoch 9/10
60000/60000 [==============================] - 5s - loss: 0.3673 - acc: 0.9035 - val_loss: 0.3514 - val_acc: 0.9076
Epoch 10/10
60000/60000 [==============================] - 5s - loss: 0.3458 - acc: 0.9070 - val_loss: 0.3296 - val_acc: 0.9112

初回実行時はMNISTデータのダウンロードが発生します。そのあと10回の学習が進んでいるのがわかります。

プログラムの解説

プログラムの詳細を見てみましょう。Kerasを使えばMNISTデータもKerasのAPIでダウンロードできます。

(x_train, y_train), (x_test, y_test) = mnist.load_data()

# 28x28 => 784
x_train = x_train.reshape(60000, 784)
x_test = x_test.reshape(10000, 784)

# one-hot ex: 3 => [0,0,0,1,0,0,0,0,0,0]
y_train = to_categorical(y_train, 10)
y_test = to_categorical(y_test, 10)

ここではダウンロード後のデータを全結合型のニューラルネットワークで処理できるようにデータを整形しています。

今回のプログラムは全結合型のニューラルネットワークです。KerasのDenseクラスで全結合レイヤーを作っています。

model.add(Dense(50, input_dim=784))

入力層のノード数はinput_dimで指定します。MNISTの画像データが28x28だから784になります。あと今回は0-9の10クラス分類なので出力層のノード数も10になります。

あとは活性化関数を指定して、レイヤーを並べています。中間層の活性化関数にはsigmoid関数、出力層の活性化関数にはsoftmax関数を指定しています。今回は多クラス分類なので出力層の活性化関数にはsoftmax関数を使っています。

モデルが完成したらコンパイルします。コンパイル時には損失関数とオプティマイザを指定します。損失関数には"categorical_crossentropy"、オプティマイザにはSGDを指定しています。metricsに指定した内容はエポック時に表示したい内容です。ここでは正答率(acc)を表示しています。

model.compile(optimizer='sgd', loss='categorical_crossentropy', metrics=['accuracy'])

ちなみに多クラス分類ではなく2値分類(yes/noみたいな)の場合は損失関数(loss)にbinary_crossentropyを指定します。その場合出力層の活性化関数をsigmoidにもできます。

あとは学習開始です。

history = model.fit(x_train, y_train, batch_size=32, validation_data=(x_test, y_test)))

MNISTの訓練データは60000件(テストデータは10000件)あるので、32件ずつランダムに取り出して勾配を求めます。求めた勾配よって重みが各ノードの重み・バイアスが更新されます(SGD)。引数にvalidation_dataを指定することでエポックごとにテストデータで検証(ホールドアウト検証)してくれます。デフォルトで10エポック(同じことを10回)学習します。

学習のグラフ化

matplotlibを使って学習の様子をグラフにしてみましょう。

import matplotlib.pyplot as plt

plt.ylim(0.0, 1)
plt.plot(history.history['acc'], label="acc")
plt.plot(history.history['val_acc'], label="val_acc")
plt.legend()

plt.show()

f:id:yamasahi:20171125154400p:plain

参考

オプティマイザについては以下のページが参考になります。

postd.cc

ImageNetから画像データをダウンロードする方法

機械学習、画像認識を始めると「大量の画像データないかなー」とググることになります。そうするとすぐにImageNetなる存在に気づきます。

ImageNet

ImageNetとはスタンフォード大学がインターネット上から画像を集め分類したデータセット。一般画像認識用に用いられる。ImageNetを利用して画像検出・識別精度を競うThe ImageNet Large Scale Visual Recognition Challenge(ILSVRC)などコンテストも開かれる。(:AI白書より引用)

ILSVRC、2012年のコンテストでディープラーニングを使ったチームが圧勝した話も有名です。

それでImageNetのサイトを訪問すると、画像を検索して、そんで画像選んで、ダウンロードしようとするとURLの一覧が表示されてー。え。これからどうしようとなります。一括ダウンロードするサンプルプログラムもネット上にいくつかありますが、Python2系のものだったり、すぐに動かなかったので自分の勉強用に作ってみました。Anacondaとか入れておいて必要なライブラリが揃っていれば動くと思います。

ダウンロードできる画像は、著作権フリーというのではないので、自己学習用のものです。

import sys
import os
from urllib import request
from PIL import Image

def download(url, decode=False):
    response = request.urlopen(url)
    if response.geturl() == "https://s.yimg.com/pw/images/en-us/photo_unavailable.png":
        # Flickr :This photo is no longer available iamge.
        raise Exception("This photo is no longer available iamge.")

    body = response.read()
    if decode == True:
        body = body.decode()
    return body

def write(path, img):
    file = open(path, 'wb')
    file.write(img)
    file.close()

# see http://image-net.org/archive/words.txt
classes = {"apple":"n07739125", "banana":"n07753592", "orange":"n07747607"}

offset = 0
max = 10
for dir, id in classes.items():
    print(dir)
    os.makedirs(dir, exist_ok=True)
    urls = download("http://www.image-net.org/api/text/imagenet.synset.geturls?wnid="+id, decode=True).split()
    print(len(urls))
    i = 0
    for url in urls:
        if i < offset:
            continue
        if i > max:
            break

        try:
            file = os.path.split(url)[1]
            path = dir + "/" + file
            write(path, download(url))
            print("done:" + str(i) + ":" + file)
        except:
            print("Unexpected error:", sys.exc_info()[0])
            print("error:" + str(i) + ":" + file)
        i = i + 1

print("end")

imagenet.pyとかで保存して、

python imagenet.py

みたいにプログラムを実行すると apple, banana, orange 3種類の画像をダウンロードしてきます。カレントフォルダ上にapple, banana, orangeフォルダができます。

apple, banana, orange以外の画像を集めたい場合は以下の部分を編集してみてください。

# see http://image-net.org/archive/words.txt
classes = {"apple":"n07739125", "banana":"n07753592", "orange":"n07747607"}

ImageNet上にwords.txtというファイルがあり、そこに画像分類ごとのIDみたいなのが振られています。

それからダウンロードする枚数は以下の部分で調整できます。

offset = 0
max = 10

なんとなく見た感じだと、apple, banana, orangeだと1000枚程度はあるので、maxを2000くらいにしておけばそれなりにデータが揃います。offsetは続きからダウンロードしたいとき用です。

あとImageNetからの画像のリンク先がFlickrであることが多く、公開停止になっているリンクも多いです。その場合、エラー画像をダウンロードしてしまうので、その部分を回避するコードです。

response = request.urlopen(url)
if response.geturl() == "https://s.yimg.com/pw/images/en-us/photo_unavailable.png":
  # Flickr :This photo is no longer available iamge.
  raise Exception("This photo is no longer available iamge.")

AIにおける知的財産の考え方について

「AI白書2017 3.2 知的財産」の自分用まとめです。AIの活用シーンにおける以下の3点についてまとめます。

  • AI生成物
  • 学習済みモデル
  • 学習用データ

f:id:yamasahi:20171110180510p:plain

注意:以下に示す内容は現在も議論されている段階です。

AI生成物の著作権保護

音楽や文学作品などのコンテンツを学習することで、新たな創作が可能になりつつあります。たとえばAIを活用して生成された音楽コンテンツには次のようなものがあります。

このようなAI生成物の著作権の取り扱いについて「次世代知財システム検討委員会報告書」には以下の記載があります。

AI生成物を生み出す過程において、学習済みモデルの利用者に創作意図があり、同時に、具体的な出力であるAI生成物を得るための創作的寄与があれば、利用者が思想感情を創作的に表現するための「道具」としてAIを使用して当該AI生成物を生み出したものと考えられることから、当該AI生成物には著作物性が認められその著作者は利用者となる

上記は著作権が認められるケースです。「創作意図」があるかどうかがポイントになるようです。

一方で、利用者の寄与が、創作的寄与が認められないような簡単な指示に留まる場合(AIのプログラムや学習済みモデルの作成者が著作者となる例外的な場合を除く)、当該AI生成物は、AIが自律的に生成した「AI創作物」であると整理され、現行の著作権法上は著作物と認められないこととなる

AIが自律的に生成した成果物は、著作物に該当せず著作権も発生しないと考えられます。

たとえば先のDaddy' Carは、作曲家(Benoit Carre氏)が「ビートルズ風」というスタイルと曲の長さを指定して生成し、作詞・編曲を行っているため、創作的寄与があると考えられます。つまり著作権が発生すると考えられます。

一方で、lamusによる楽曲はAIのみで自律的に制作されているそうです。そのため「AI創作物」として著作権は認められない、と考えられます。

学習済みモデルの保護

学習済みのモデルはプログラムとパラメータで構成されており、著作権法上の「プログラムの著作物」に該当するか議論されています。仮に該当しないとしても、特許法上では「プログラム等」に該当するならば、特許法の要件を満たすなら保護される可能性があります。

また上記に該当しない場合でも不正競争防止法条の秘密管理性、有用性、非公知性といった要件を満たす場合は「営業秘密」として保護されるようです。

(正直、この辺は専門外なのでちょっと難しい。。)

蒸留モデル

学習済みモデルに対して、データの入出力を繰り返し、その結果を別の学習モデルに学習させることもできます。このように作られたものは「蒸留モデル」と呼ばれます。

蒸留モデルは、元のモデルからの依拠性を証明することが難しく、著作権による保護が困難になります。一方で、特許権による保護は、依拠性の立証がなくても認められるため、特許権の範囲での対応も議論されています。

また学習済みモデルの利用規約により、蒸留モデルを禁止する等の契約で保護することもできます。この場合契約当事者以外の第三者から守ることはできないものの、柔軟な対応が可能となります。

学習データについて

AI白書には以下の一文があります。

インターネット上のデータ等の著作物を元に学習用データを作成・解析することは営利目的の場合も含めて、著作権法47条の7に基づいて著作権侵害には当たらないとされており、機械学習活用の促進にとって我が国特有の有用な制度となっている。(AI白書より)

学習データについては著作権法47条の7がポイントのようです。以前、こちらの記事も話題になっていました。

www.itmedia.co.jp

日本はインターネット上のデータを機械学習に活用しやすい一方で、AIの研究開発推進に向けての国産の共有データセット(ImageNetのようなもの)の整備が遅れているとの指摘もあります。

それから著作権法47条の7の規定は、もともと機械学習の促進を想定したものではないため、解釈の仕方や議論の余地が残っています。

共有データセットについての問題

海外にはImageNetやMNIST、MS COCOといった共有データセットが存在します。また、これらのデータを活用して、事前に学習済みのモデルも公開されています。

これらの学習済みモデルは、欧米で作られた共有データセットで作られている点を理解しておく必要があります。そのため日本固有の「ラーメン」のような画像認識ができない、といった問題があります。

参考書籍

AI白書 2017

AI白書 2017

Jupyter Notebookで始める機械学習プログラミング

これからPython機械学習始めたい方向けのまとめです。

Python

PythonはWebアプリの開発からちょっとしたツールの開発まで、多目的に利用できるプログラミング言語です。また機械学習ライブラリが充実しているため近年注目を集めています。

www.python.org

次のAnacondaディストリビューションPythonをインストールすることもできます。

Anaconda

Python機械学習を始めるにはCONTINUUM社の配布しているはAnacondaディストリビューションを活用すると良いでしょう。以下のURLからダウンロートすることができます。

www.anaconda.com

AnacondaにはPython本体だけでなく機械学習に必要なライブラリも梱包されているため、Anacondaをインストールすればすぐに機械学習プログラミングを始めることができます。

Jupyter Notebook

AnacondaにはJupyter Notebookというブラウザ上で動作するPython開発環境も付属しています。ターミナル(コマンドプロンプト)上で jupyter-notebook コマンドを実行するとローカルでサーバプログラムが起動し、ブラウザが起動します。

jupyter.org

Jupyter Notebookの起動方法について補足しておきます。Anacondaインストール後、Windowsでは画面左下のWindowsメニューから「jupyter-notebook」とタイプするとアプリが見つかります。Macでは、ターミナル上でjupyter-notebookコマンドをタイプします。Jupyter Notebookはデフォルトで8888番ポートを使って起動します。ブラウザで http://localhost:8888/ にアクセスすると開発画面が表示されるでしょう。

Jupyter Notebookでは、ノートブックという形式でファイルを作成できます。ノートブックにはPythonコードや実行結果だけでなく、Markdown形式で文章を挿入することもできるので、データ分析の評価レポートを作成しやすいようになっています。またノートブックファイルは拡張子.ipynbという形式保存されます。.ipynbファイルはGitHubやGistなどでも表示できます。

クラウド上でJupyter Notebookを起動する

Jupyter Notebookの実体はブラウザで利用するWebアプリケーションです。そのためサーバ上でJupyter Notebookを起動すれば、手元のパソコンのブラウザからサーバ上でPythonプログラムを実行することも可能です。

ここではあらかじめJupyter環境をインストール済みのDockerコンテナを起動する方法を紹介します。またDeepLearning開発を支援するPaaS型サービスであるFloydHubを紹介します。

DockerでJupyter環境を構築するには

ここではクラウド環境としてAWSのEC2インスタンスUbuntu / Small)があるものとします。

sshでEC2インスンタンスにログインします。まずUbuntuのパッケージ情報を更新しておきます。

sudo apt update

次にDockerをインストールします。

sudo apt install docker.io -y

Dockerコンテナ(murayama333/ai-prog)を起動します。

sudo docker run -p 8888:8888 --name ai-prog -it murayama333/ai-prog

Dockerの出力ログにURLが表示されます。URLはlocalhostとなっているので、EC2のグローバルIPに置き換えてアクセスすると完了です。

ここで利用したDockerイメージはこちらの記事を参考に作っています。勉強になりました。

dr-asa.hatenablog.com

FloydHubで開発する

Dockerを使えばあっという間に開発環境を構築できますが、FloydHubを使えばもっと手軽にJupyter環境を整えることもできます。

FloydHub - Deep Learning Platform - Cloud GPU

FloydHubを一言で説明するなら

FloydHub is Heroku for DL

FloydHubはDeepLearningのためのHerokuです。つまり、DeepLearningのためのPaaS(Platform as a Service)です。FloydHubの素晴らしいところは、クラウド上ですぐに開発&実行ができるだけでなく、GPUインスタンスを利用することができる点です。無料アカウントの場合、GPUインスタンスは 2時間 / 月 まで利用できます。

FloydHub - Deep Learning Platform - Cloud GPU

FloydHub上でアカウントを作成後、開発キット(floyd-cli)をインストールします。

pip install -U floyd-cli

pipはPythonライブラリの追加コマンドです。pipコマンドを利用するにはローカルにPythonがインストールされている必要があります。

floydコマンドが有効になるので、FloydHubにログインします。

$ floyd login

以下、オフィシャルのクイックスタートを参考に動かしてみましょう。

https://docs.floydhub.com/getstarted/quick_start/

GitHubからサンプルのリポジトリをダウンロードしておきます。

git clone https://github.com/floydhub/quick-start.git
cd quick-start

リポジトリの中のファイルの一覧を確認しましょう。

ls
eval.py  LICENSE  mnist_cnn.ipynb  README.md  train_and_eval.py  train.py

以降は、上記の train_and_eval.py プログラムをFloydHub上で実行してみます。

FloydHubプロジェクトを初期化します。

floyd init mnist-cnn

FloydHubでPythonプログラムを実行してみましょう。

floyd run --gpu --env tensorflow-1.3 "python train_and_eval.py"

ここでは --gpu オプションを指定してるので、GPUインスタンス上で実行されます。

FloydHubでJupyter Notebookを起動する

FloydHubのプロジェクトフォルダ上で(先のquick-startフォルダなど)、次のようにコマンドを実行します。

floyd run --mode jupyter

Creating project run. Total upload size: 198.0B
Syncing code ...
[================================] 946/946 - 00:00:00

JOB NAME
-----------------------------------
murayama333/projects/mnist-cnn/1

Setting up your instance and waiting for Jupyter notebook to become available .............

Path to jupyter notebook: https://floydlabs.com/notebooks/xxx

数秒(数十秒)でJupyterが起動します。

GPUインスタンスで起動する場合は、次のように指定します。

floyd run --gpu --mode jupyter

Jupyterを起動している間、GPUインスタンスの利用時間がカウントされるので、利用を終えたらインスタンスを停止しておきましょう。停止するときは JOB NAME(floyd run時のログを参照)を指定します。

floyd stop murayama333/projects/mnist-cnn/1

Reactだけ学ぶハンズオン

Reactだけ学ぶハンズオン

  1. Hello React・・・はじめてのReact
  2. Greeting・・・プロパティを学ぶ
  3. GreetingList・・・コレクションの考え方を学ぶ
  4. Echo・・・ステートを学ぶ
  5. Welcome・・・コンポジットなコンポーネントの作り方を学ぶ

昔の勉強会の資料です。内容古かったらすみません。

こっちのが見やすいかもです。

https://github.com/murayama333/react-handson