質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.50%
Chainer

Chainerは、国産の深層学習フレームワークです。あらゆるニューラルネットワークをPythonで柔軟に書くことができ、学習させることが可能。GPUをサポートしており、複数のGPUを用いた学習も直感的に記述できます。

Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

機械学習

機械学習は、データからパターンを自動的に発見し、そこから知能的な判断を下すためのコンピューターアルゴリズムを指します。人工知能における課題のひとつです。

Q&A

解決済

4回答

4109閲覧

ニューラルネットワークを使い「掛け算」の学習をしたい

LaLaLand

総合スコア107

Chainer

Chainerは、国産の深層学習フレームワークです。あらゆるニューラルネットワークをPythonで柔軟に書くことができ、学習させることが可能。GPUをサポートしており、複数のGPUを用いた学習も直感的に記述できます。

Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

機械学習

機械学習は、データからパターンを自動的に発見し、そこから知能的な判断を下すためのコンピューターアルゴリズムを指します。人工知能における課題のひとつです。

0グッド

0クリップ

投稿2017/11/17 06:33

chainerを使って、ニューラルネットワークの勉強をしています。
先日はモデル・オプティマイザーの保存についてご教示いただきありがとうございました。

引き続き、色々調査しているのですが、単純な「掛け算」の学習ができずに困っています。

作成したデータは以下の通り。
・入力:4次元データ
・出力:入力の各次元の値を掛けただけのもの

入力が全て正の場合は、以下の通り、logとexpを組み合わせることで簡単に学習できました。

python

1class MyChain(Chain): 2 def __init__(self): 3 super(MyChain, self).__init__( 4 l1 = L.Linear(4, 100), 5 l2 = L.Linear(100, 1)) 6 7 def __call__(self, x, y): 8 xv = Variable(x) 9 yv = Variable(y) 10 pr = self.predict(xv) 11 return F.mean_squared_error(pr, yv) 12 13 def predict(self, x): 14 h0 = F.log(x) 15 h1 = self.l1(h0) 16 h2 = F.exp(self.l2(h1)) 17 return h2

しかし、入力に負の値が含まれている場合には、このやり方では学習できません。(logに0以下を入力できないので)
どんなネットワークを作れば学習できそうかを教えてください。

↓上手く学習できた時のコード↓

python

1from chainer import Chain 2from chainer import cuda, Function, gradient_check, Variable, optimizers, serializers, utils 3import chainer.links as L 4import chainer.functions as F 5import numpy as np 6import os 7 8 9class MyChain(Chain): 10 def __init__(self): 11 super(MyChain, self).__init__( 12 l1 = L.Linear(4, 100), 13 l2 = L.Linear(100, 1)) 14 15 def __call__(self, x, y): 16 xv = Variable(x) 17 yv = Variable(y) 18 pr = self.predict(xv) 19 return F.mean_squared_error(pr, yv) 20 21 def predict(self, x): 22 h0 = F.log(x) 23 h1 = self.l1(h0) 24 h2 = F.exp(self.l2(h1)) 25 return h2 26 27# 全て正の値でサンプルデータを作る関数 28# ノイズ無し 29def createData__Positive_WithoutNoise(N): 30 # 適当にデータを作って掛け算を学習してみる。 31 # x1, x2, x3, x4が入力データ。 32 # 期待する出力(教師)は単なる掛け算結果。 33 x1 = np.linspace(1, 100, N).astype(np.float32) 34 x2 = np.linspace(0.1, 10, N).astype(np.float32) 35 x3 = np.linspace(0.1, 10, N).astype(np.float32) 36 x4 = np.linspace(1, 100, N).astype(np.float32) 37 np.random.shuffle(x2) 38 np.random.shuffle(x3) 39 np.random.shuffle(x4) 40 return np.c_[x1, x2, x3, x4], np.c_[x1*x2*x3*x4] 41 42 43 44N = 1500 45NTest = 1200 46batchSize = 10 47xtrain, ytrain = createData__Positive_WithoutNoise(N) 48xtest, ytest = createData__Positive_WithoutNoise(NTest) 49 50 51# モデル作成 52modelFileName = "kakezan4-model.npz" 53if os.path.exists(modelFileName): 54 model = MyChain() 55 serializers.load_npz(modelFileName, model) 56else: 57 model = MyChain() 58 59# optimizer作成 60optimizer = optimizers.SMORMS3() 61optimizer.setup(model) 62optimizerFileName = "kakezan4-optimizer.npz" 63if os.path.exists(optimizerFileName): 64 serializers.load_npz(optimizerFileName, optimizer) 65 66for epoch in range(10000): 67 perm = np.random.permutation(N) 68 for i in range(0, N, batchSize): 69 x_batch = xtrain[perm[i:i + batchSize]] 70 y_batch = ytrain[perm[i:i + batchSize]] 71 72 model.zerograds() 73 loss = model(x_batch, y_batch) 74 loss.backward() 75 optimizer.update() 76 77 if 0 < epoch and epoch % 50 == 0: 78 loss = model(xtest, ytest) 79 serializers.save_npz(modelFileName, model) 80 serializers.save_npz(optimizerFileName, optimizer) 81 xtrain, ytrain = createData__Positive_WithoutNoise(N) 82 xtest, ytest = createData__Positive_WithoutNoise(NTest) 83 84 print("{}\t{}".format(epoch, loss.data))

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答4

0

ベストアンサー

mkgreiさんの答えにヒントを得ていますが、入力値の符号を取り出して、答えの符号だけを目的関数とする学習器を別に用意させることで解決できそうです。

同一NNの各層に追加の符号学習専用の入力層、中間層、出力層を用意する、といいかえてもよいと思います。

<追記>
以下の記事が参考になると思います。
https://qiita.com/ta-ka/items/bcdfd2d9903146c51dcb
リンク先ページの削除されていると参考にできなくなるとおもいますので、コード部分のみ転記しておきます。(記載先の例で示されている学習率で十分かどうかは確認をお願いいたします。)

neuralnetwork.py

python

1import numpy 2import math 3import random 4from matplotlib import pyplot 5 6class Neural: 7 8 # constructor 9 def __init__(self, n_input, n_hidden, n_output): 10 self.hidden_weight = numpy.random.random_sample((n_hidden, n_input + 1)) 11 self.output_weight = numpy.random.random_sample((n_output, n_hidden + 1)) 12 self.hidden_momentum = numpy.zeros((n_hidden, n_input + 1)) 13 self.output_momentum = numpy.zeros((n_output, n_hidden + 1)) 14 15 16# public method 17 def train(self, X, T, epsilon, mu, epoch): 18 self.error = numpy.zeros(epoch) 19 N = X.shape[0] 20 for epo in range(epoch): 21 for i in range(N): 22 x = X[i, :] 23 t = T[i, :] 24 25 self.__update_weight(x, t, epsilon, mu) 26 27 self.error[epo] = self.__calc_error(X, T) 28 29 30 def predict(self, X): 31 N = X.shape[0] 32 C = numpy.zeros(N).astype('int') 33 Y = numpy.zeros((N, X.shape[1])) 34 for i in range(N): 35 x = X[i, :] 36 z, y = self.__forward(x) 37 38 Y[i] = y 39 C[i] = y.argmax() 40 41 return (C, Y) 42 43 44 def error_graph(self): 45 pyplot.ylim(0.0, 2.0) 46 pyplot.plot(numpy.arange(0, self.error.shape[0]), self.error) 47 pyplot.show() 48 49 50# private method 51 def __sigmoid(self, arr): 52 return numpy.vectorize(lambda x: 1.0 / (1.0 + math.exp(-x)))(arr) 53 54 55 def __forward(self, x): 56 # z: output in hidden layer, y: output in output layer 57 z = self.__sigmoid(self.hidden_weight.dot(numpy.r_[numpy.array([1]), x])) 58 y = self.__sigmoid(self.output_weight.dot(numpy.r_[numpy.array([1]), z])) 59 60 return (z, y) 61 62 def __update_weight(self, x, t, epsilon, mu): 63 z, y = self.__forward(x) 64 65 # update output_weight 66 output_delta = (y - t) * y * (1.0 - y) 67 _output_weight = self.output_weight 68 self.output_weight -= epsilon * output_delta.reshape((-1, 1)) * numpy.r_[numpy.array([1]), z] - mu * self.output_momentum 69 self.output_momentum = self.output_weight - _output_weight 70 71 # update hidden_weight 72 hidden_delta = (self.output_weight[:, 1:].T.dot(output_delta)) * z * (1.0 - z) 73 _hidden_weight = self.hidden_weight 74 self.hidden_weight -= epsilon * hidden_delta.reshape((-1, 1)) * numpy.r_[numpy.array([1]), x] 75 self.hidden_momentum = self.hidden_weight - _hidden_weight 76 77 78 def __calc_error(self, X, T): 79 N = X.shape[0] 80 err = 0.0 81 for i in range(N): 82 x = X[i, :] 83 t = T[i, :] 84 85 z, y = self.__forward(x) 86 err += (y - t).dot((y - t).reshape((-1, 1))) / 2.0 87 88 return err 89

main.py

python

1from neuralnetwork import * 2 3if __name__ == '__main__': 4 5 X = numpy.array([[0, 0], [0, 1], [1, 0], [1, 1]]) 6 T = numpy.array([[1, 0], [0, 1], [0, 1], [1, 0]]) 7 N = X.shape[0] # number of data 8 9 input_size = X.shape[1] 10 hidden_size = 2 11 output_size = 2 12 epsilon = 0.1 13 mu = 0.9 14 epoch = 10000 15 16 nn = Neural(input_size, hidden_size, output_size) 17 nn.train(X, T, epsilon, mu, epoch) 18 nn.error_graph() 19 20 C, Y = nn.predict(X) 21 22 for i in range(N): 23 x = X[i, :] 24 y = Y[i, :] 25 c = C[i] 26 27 print(x) 28 print(y) 29 print(c) 30 print("")

追記2
手元で確認しましたら、2値入力の符号計算については、
epsilon = 0.1
mu = 0.5
で10000回で良好な結果が得られました。
[0 0][ 0.94077065 0.05854585]0
[0 1][ 0.05252437 0.94818359]1
[1 0][ 0.05249829 0.94820941]1
[1 1][ 0.94447755 0.05472062]0

追記3(2017/11/20 19:25)
上記は2値入力でしたので4値入力でうまくいくパターンを見つけましたので、追記しておきます。

既に不要かもしれませんが、完全な学習ができたハイパーパラメータを記載しておきます。

Python

1 X = numpy.array([[0, 0, 0, 0], [0, 0, 0, 1], [0, 0, 1, 0],[0, 0, 1,1], # 入力の記号:0がマイナス、1がプラス 2 [0, 1, 0, 0], [0, 1, 0, 1], [0, 1, 1, 0], [0, 1, 1, 1], 3 [1, 0, 0, 0], [1, 0, 0, 1], [1, 0, 1, 0], [1, 0, 1, 1], 4 [1, 1, 0, 0], [1, 1, 0, 1], [1, 1, 1, 0], [1, 1, 1, 1]]) 5 T = numpy.array([[1, 0, 0, 0], [0, 1, 0, 0], [0, 1, 0, 0], [1, 0, 0, 0], # 答えの符号:1個目が1ならプラス、 6 [0, 1, 0, 0], [1, 0, 0, 0], [1, 0, 0, 0], [0, 1, 0, 0], # 2個目が1ならマイナス 7 [0, 1, 0, 0], [1, 0, 0, 0], [1, 0, 0, 0], [0, 1, 0, 0], 8 [1, 0, 0, 0], [0, 1, 0, 0], [0, 1, 0, 0], [1, 0, 0, 0]]) 9 # X = numpy.array([[0, 0], [0, 1], [1, 0], [1, 1]]) # 入力の記号:0がマイナス、1がプラス 10 # T = numpy.array([[1, 0], [0, 1], [0, 1], [1, 0]]) # 答えの符号:1個目が1ならプラス、2個目が1ならマイナス 11 N = X.shape[0] # number of data 12 13 input_size = X.shape[1] 14 hidden_size = 6 # 2 15 output_size = 4 # 2 16 epsilon = 0.3 # 0.1 17 mu = 0.5 # 0.9 18 epoch = 100000 # 10000

学習結果は以下となります。

[0 0 0 0][ 0.9946526 0.00533347 0.00283314 0.00259751]0 [0 0 0 1][ 3.35769941e-03 9.96644302e-01 5.56622699e-04 6.70592964e-04]1 [0 0 1 0][ 3.33927080e-03 9.96660884e-01 5.59857461e-04 6.70702223e-04]1 [0 0 1 1][ 9.96596879e-01 3.40821671e-03 2.22956843e-04 3.25614771e-04]0 [0 1 0 0][ 3.27048326e-03 9.96728618e-01 5.64677142e-04 6.74756067e-04]1 [0 1 0 1][ 9.96570795e-01 3.43393502e-03 2.23697557e-04 3.26314940e-04]0 [0 1 1 0][ 9.96504986e-01 3.49893558e-03 2.25097803e-04 3.27462076e-04]0 [0 1 1 1][ 5.66155206e-03 9.94335298e-01 9.34340392e-05 9.24230822e-05]1 [1 0 0 0][ 3.31009107e-03 9.96690587e-01 5.56904682e-04 6.68238304e-04]1 [1 0 0 1][ 9.96573108e-01 3.43220946e-03 2.22530928e-04 3.25064048e-04]0 [1 0 1 0][ 9.96511494e-01 3.49322967e-03 2.23526919e-04 3.25795278e-04]0 [1 0 1 1][ 5.66332128e-03 9.94333992e-01 9.32042942e-05 9.22570059e-05]1 [1 1 0 0][ 9.96481951e-01 3.52230033e-03 2.24429722e-04 3.26645683e-04]0 [1 1 0 1][ 5.66085770e-03 9.94336166e-01 9.33024233e-05 9.23137346e-05]1 [1 1 1 0][ 5.65317314e-03 9.94343241e-01 9.34888288e-05 9.24102042e-05]1 [1 1 1 1][ 9.91126091e-01 8.87614458e-03 2.20889172e-05 2.87712893e-05]0

mkgreiさんとのコメント欄でのやり取りで、興味がでたので、XNORを出力するMLPの重み付けパラメータの一例を記載しておきます。

入力層+隠れ層(一層)+出力層の形での解の一例示です。

<イメージ図>(※注:\はバックスラッシュ) 入力層 隠れ層 出力層 (w11) x1 ― ― h1 (w21)\ / (v1) X    o1 (w12)/ \ /(v2) x2 ― ― h2 (w22)

h1 = f(w11 * x1 + w12 * x2 + b1)
h2 = f(w12 * x1 + w22 * x2 + b2)
o1 = f(v1 * h1 + v2 * h2 + b3)

fはシグモイド関数です。上記の形でMLPを表すことができます。

このときにXORを実現するパラメータの組み合わせは以下です。

w11 = 2
w12 = 2
w21 = -2
w22 = -2
b1 = -1
b2 = 3
v1 = -2
v2 = -2
b3 = 3

ここに負数があるのでreluではだめなんでしょうね。

投稿2017/11/17 07:13

編集2017/11/27 01:14
yag1kaz

総合スコア253

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

yag1kaz

2017/11/18 15:31

掛け算の符号のみの判定はXNORの学習器と等価になります。 もともと、単純パーセプトロンでは、XORが学習できないという批判を学者さんにされて、その解決策としてDNNが登場したものの、勾配消失による学習不良で、DNNは冬の時代に突入したけれど、バックプロパゲーションでそれをのりこえてDNNブームが起きたと認識しています。 なので、XNORもDNNなら学習可能、という風に理解しています。 http://okoysm.hatenablog.jp/entry/2017/01/26/081849#パーセプトロンの限界 XNORとしては、FPGA化まわりとか、バイナリNetとか周りでは別の思惑(高速化や省電力化)があって、そこはそこで熱いのですが、今回のケースではお勉強目的のようですから、上記の追記と記事が参考になると思います。
mkgrei

2017/11/20 07:18

大変参考になりました。 確かに学習できますね。 ところで、sigmoidを活性化関数にすると学習できるのに対して、reluだといけない直感的な理由というのはあるのでしょうか? ざっと試してみると、reluではうまく学習できませんでした。 パラメータチューニングが足りていないだけでしょうか?
mkgrei

2017/11/20 09:55

追記 隠れ層2に2ニューロンずつにすると、活性化関数がsigmoidの時に、重みの初期化次第で変なところに収束することがあるようです。 重み初期化の乱数をかえると、多くの場合には正しく学習できるようです。 Dropout層を入れると安定するのかもしれません。
yag1kaz

2017/11/20 10:32

reluでうまくいかない直感的な理由はわかりません。 sigmoidでもパラメータチューニングは必要でしたので、同様の可能性はあると思います。
mkgrei

2017/11/20 11:06

reluの勾配が定数であることに問題があると妄想していましたが、あまり明確でないかもしれないですね。
yag1kaz

2017/11/26 08:01 編集

いろいろと調べてみて、直感的にreluだと駄目な理由に思いいたりました。xが負の領域で解が0になるため、学習が正しく進まないのだろう、と想像しています。
yag1kaz

2017/11/27 01:17

今、chainerが手元に無いので確認ができないのですが関数系を見る限りでは直感的にhard-sigmoidsでもうまくいくと思います。 学習がうまくいくハイパーパラメータの提示もおわっておりますので、なぜhard-sigmoidsを合わせてみたいのか?という動機をおしえていただけますか?
mkgrei

2017/11/28 11:20

たしかにおっしゃる通りな気がします。 中間層の数と活性化関数次第で、XORを表現できるかどうかの境目なのですね。 余裕を持たせることが重要な気がしてます。 おもしろいですね。
LaLaLand

2017/12/02 06:59

失礼しました。確かにハイパーパラメータを提示していただいておりますので hard sigmoidは必要ありませんでしたね。 本当に長い時間、お付き合いいただきありがとうございました
guest

0

すいません、yag1kaz様よりコードをいただいていたことを見落としていました。
ちょっと確認します。

投稿2017/11/20 04:52

LaLaLand

総合スコア107

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

yag1kaz

2017/11/25 10:09

回答に追記を行っていますので、ご確認下さい。
guest

0

mkgrei様、yag1kaz様、回答ありがとうございます。
ひとまず、yag1kaz様の助言を参考にして、符号のみを学習させるように適当な
ネットワークを組んでみました。

softmax_cross_entropyを利用すれば、かなりの精度で符号学習ができることをつかみました。
一方、mean_squared_errorでは、あまり精度が良くないようです。
引き続き、調査をしていきます。

Python

1class MyChain(Chain): 2 def __init__(self): 3 super(MyChain, self).__init__( 4 l1 = L.Linear(4, 100), 5 l2 = L.Linear(100, 100), 6 # softmax_cross_entropyを使う場合は、↓ 7 # l3 = L.Linear(100, 3)) 8 9 # mean_squared_errorを使う場合は、↓ 10 l3 = L.Linear(100, 1)) 11 12 def __call__(self, x, y): 13 pr = self.predict(x) 14 yv = Variable(y) 15 # softmax_cross_entropyで正しく学習できた。 16 #return F.softmax_cross_entropy(pr, yv) 17 return F.mean_squared_error(pr, yv) 18 19 def predict(self, x): 20 h1 = F.hard_sigmoid( self.l1(x) ) 21 h2 = self.l2(h1) 22 h3 = self.l3(h2) 23 return h3 24 25# 正負両方の値でサンプルデータを作る関数 26def createData__PositiveNegative_WithoutNoise(N): 27 # 適当にデータを作って掛け算を学習してみる。 28 # x1, x2, x3, x4が入力データ。 29 # 期待する出力(教師)は単なる掛け算結果。 30 x1 = np.linspace(-1, 100, N).astype(np.float32) 31 x2 = np.linspace(-0.1, 10, N).astype(np.float32) 32 x3 = np.linspace(-10, 0.1, N).astype(np.float32) 33 x4 = [] 34 35 for i in range(N): 36 x4.append( i % 11 ) 37 38 x4 = np.array(x4, dtype=np.float32) 39 40 np.random.shuffle(x2) 41 np.random.shuffle(x3) 42 np.random.shuffle(x4) 43 44 y = np.sign(x1*x2*x3*x4) 45 46 # softmax_cross_entropyを使う場合は、↓ 47 # return np.c_[x1, x2, x3, x4], np.array(y).astype(np.int32) 48 49 # mean_squared_errorを使う場合は、↓ 50 return np.c_[x1, x2, x3, x4], np.c_[ np.array(y).astype(np.float32) ] 51

投稿2017/11/20 04:51

LaLaLand

総合スコア107

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

mkgrei

2017/11/20 09:31 編集

MSEでも答えが正か負かはわかりますね。(訂正後) 規格化とかはできないので、1と-1の近くでブレがありますが。 活性化関数にreluを使うと、中間層のニューロンの数を2にすると調子が悪いようです。 10とかに増やすと学習できるようになりました。(ただ疑問も…) さらに、このモデルについてですが、Xに対して、signを予め取っておかないと、学習データの外の数字に対して安定して符号判定できないかもしれません。 例えば、x1=1000とか。 以下、混乱中 中間層を増やしすぎると学習しているのか過学習しているのか判別がつきませんが… 最初にsignを取ってしまうと、全パターンを学習器にみせてしまうので、中間層を十分増やせば、教師データに完全学習して精度100%。 そもそも論理回路の入力対出力を学習する場合過学習とかいう概念はないのだろうか。(そんなにパターンがないし)
yag1kaz

2017/11/20 10:39

過学習については今回は考えなくてもよいと思います。 なぜならXOR(XNOR)の論理回路がそもそも非線形出力を期待しているので、過学習しないと正解を出力できないからです。 また、回答パターンの組み合わせも16個の組み合わせしかないので、訓練データセットにテストデータセットがすぐに包含されてしまいます。 突き詰めると、最初から正解を提出できる重みをニューロンに指定するだけでよい、ということになってしまい、mkgreiさんの最初のご回答にきわめて近い状態、ということになってしまうのですが、今回はプロセスが大事という事で。
mkgrei

2017/11/20 11:11

なるほど、ごもっともですね。 いろんなことがごちゃまぜになって混乱していました。 単層のネットワークでは関数系としてそもそもXORを表現できなかったことに対して、多層にすると学習できるようになるというのがポイントだったんですね。 スッキリしました。 ありがとうございます。
guest

0

入力に対して、符号は機械的に処理してしまうのはいけませんか?
そうすれば、入力をすべて正にすることが可能に…

投稿2017/11/17 06:56

mkgrei

総合スコア8560

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

LaLaLand

2017/11/17 12:07

ありがとうございます。 仰る通りだとは思いますが、あくまで機械学習のお勉強の例題として掛け算を設定していますので、 出来れば、機械学習っぽいやり方を模索しています。 単に掛け算するだけなら、普通にかければいいので・・・
mkgrei

2017/11/20 07:35

たしかにおっしゃるとおりですね。 今は掛け算なので、事前に符号と大きさとを分離して考えることができるとわかっていますが。 指数にすると、掛け算が線形になることも利用していて。 どこまで事前情報を利用してよいのか混乱しました。 何がともあれ、すごくおもしろい問題設定だと思っています。 いろいろと考えさせられました。 ネット上でもっと複雑であるが、おもしろそうな結果が出る例題が多く、基礎的なところがおろそかでした。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問