ruby-dnn 0.1.8 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a9bd42ca5cbea3b4a3abd984157df19cf87ca2a6ba80a3a76cf155e020582508
4
- data.tar.gz: 2be2b617347618bf18d140e1ed4b5ede5d3a0f6540d6bc18217ae9b86c22aa2d
3
+ metadata.gz: c62e4bbbd5aa1e89dbff3e112c161f14fef9b4165721fed81815b6103af184f5
4
+ data.tar.gz: d5b46dfde07d33f9309adb4af6256fa404218b9ad1d1cfc82a79b6752fc4549e
5
5
  SHA512:
6
- metadata.gz: 9077ebdf09fbc33d5b163c9ef2bd9ec4bb6681b87e5f638db25d6fbc46603ba50a8a6cb81123a42e07984636d2055fae3ed54712f11df815d856854a5845973d
7
- data.tar.gz: 5b3c56027727a7dee1f1cbae31c6ddd887d5ea7cf015c1d111942e6c518579ffc607c8c38c13702e77f86ad9b4428d27296287fd3c2b13e02a77992b5360900b
6
+ metadata.gz: 49515dd6ed15f07b551b88d3b724c280f417a07b12c869974d5b4f34a2c2bc6ff4747ba6c853840588b365e3d77ee50cebded9d82f739e79142aebaf505313b5
7
+ data.tar.gz: dbd7084f28a5a1c9b5675eadb9c20e76c42897ba32a20a552ed183ada75dc83aa7953718e853a9a658dcdb794317943c56f4e46ebeedf8d6a663eb02f90832c0
data/API-Reference.ja.md CHANGED
@@ -2,7 +2,7 @@
2
2
  ruby-dnnのAPIリファレンスです。このリファレンスでは、APIを利用するうえで必要となるクラスとメソッドしか記載していません。
3
3
  そのため、プログラムの詳細が必要な場合は、ソースコードを参照してください。
4
4
 
5
- 対応バージョン:0.1.7
5
+ 対応バージョン:0.2.0
6
6
 
7
7
  # module DNN
8
8
  ruby-dnnの名前空間をなすモジュールです。
@@ -90,7 +90,15 @@ Model
90
90
  ### return
91
91
  なし。
92
92
 
93
- ## def train(x, y, epochs, batch_size: 1, batch_proc: nil, verbose: true, &epoch_proc)
93
+ ## def compiled?
94
+ モデルがコンパイル済みであるか否かを取得します。
95
+ ### arguments
96
+ なし。
97
+ ### return
98
+ bool
99
+ モデルがコンパイル済みであるか否か。
100
+
101
+ ## def train(x, y, epochs, batch_size: 1, test: nil, verbose: true, batch_proc: nil, &epoch_proc)
94
102
  コンパイルしたモデルを用いて学習を行います。
95
103
  ### arguments
96
104
  * SFloat x
@@ -101,10 +109,12 @@ Model
101
109
  学習回数。
102
110
  * Integer batch_size: 1
103
111
  学習に使用するミニバッチの数。
104
- * Proc batch_proc: nil
105
- 一度のバッチ学習が行われる前に呼び出されるprocを登録します。
112
+ * Array test: nil
113
+ [テスト用入力データ, テスト用出力データ]の形式で設定すると、1エポックごとにテストを行います。
106
114
  * bool verbose: true
107
115
  trueを設定すると、学習ログを出力します。
116
+ * Proc batch_proc: nil
117
+ 一度のバッチ学習が行われる前に呼び出されるprocを登録します。
108
118
  ### block
109
119
  epoch_proc
110
120
  1エポックの学習が終了するたびに呼び出されます。
@@ -126,23 +136,8 @@ epoch_proc
126
136
  Integer
127
137
  損失関数の値を返します。
128
138
 
129
- ## def test(x, y, batch_size = nil, &batch_proc)
130
- 学習結果をもとにテストを行います。
131
- ### arguments
132
- * SFloat x
133
- テスト用入力データ。
134
- * SFloat y
135
- テスト用出力データ。
136
- * batch_size
137
- ミニバッチの数。学習を行っていないモデルのテストを行いたい場合等に使用します。
138
- ### block
139
- 一度のバッチ学習が行われる前に呼び出されます。
140
- ### return
141
- Float
142
- テスト結果の認識率を返します。
143
-
144
139
  ## def accurate(x, y, batch_size = nil, &batch_proc)
145
- 学習結果をもとに認識を返します。
140
+ 学習結果をもとに認識率を返します。
146
141
  ### arguments
147
142
  * SFloat x
148
143
  テスト用入力データ。
@@ -175,14 +170,22 @@ SFloat
175
170
 
176
171
  ## 【Instance methods】
177
172
 
178
- ## def init(model)
179
- モデルのコンパイル時に、レイヤーを初期化するために使用されます。
173
+ ## def build(model)
174
+ モデルのコンパイル時に、レイヤーをビルドするために使用されます。
180
175
  ### arguments
181
176
  * Model model
182
177
  レイヤーを持つモデルを登録します。
183
178
  ### return
184
179
  なし。
185
180
 
181
+ ## def builded?
182
+ レイヤーがビルド済みであるか否かを取得します。
183
+ ### arguments
184
+ なし。
185
+ ### return
186
+ bool
187
+ レイヤーがビルド済みであるか否か。
188
+
186
189
  ## abstruct def forward(x)
187
190
  順方向伝搬を行うメソッドです。Layerクラスを継承するクラスは、このメソッドを実装する必要があります。
188
191
  ### arguments
@@ -274,15 +277,15 @@ nilを指定すると、Zerosイニシャライザーが使用されます。
274
277
  畳み込みレイヤーを扱うクラスです。
275
278
 
276
279
  ## 【Instance methods】
277
- ## def initialize(num_filters, filter_height, filter_width, weight_initializer: nil, bias_initializer: nil, strides: [1, 1], padding 0, weight_decay: 0)
280
+ ## def initialize(num_filters, filter_width, filter_height, weight_initializer: nil, bias_initializer: nil, strides: [1, 1], padding false, weight_decay: 0)
278
281
  コンストラクタ。
279
282
  ### arguments
280
283
  * Integer num_filters
281
284
  出力するフィルターの枚数
282
- * Integer filter_height
283
- フィルターの縦の長さ
284
285
  * Integer filter_width
285
286
  フィルターの横の長さ
287
+ * Integer filter_height
288
+ フィルターの縦の長さ
286
289
  * Initializer weight_initializer: nil
287
290
  重みの初期化に使用するイニシャライザーを設定します
288
291
  nilを指定すると、RandomNormalイニシャライザーが使用されます。
@@ -290,8 +293,9 @@ nilを指定すると、RandomNormalイニシャライザーが使用されま
290
293
  バイアスの初期化に使用するイニシャライザーを設定します。
291
294
  * Array<Integer> strides: [1, 1]
292
295
  畳み込みを行う際のストライドの単位を指定します。配列の要素0でy軸方向のストライドを設定し、要素1でx軸方向のストライドを設定します。
293
- * Integer padding: 0
294
- イメージに対してゼロパディングを行う単位を指定します。
296
+ * bool padding: true
297
+ イメージに対してゼロパディングを行うか否かを設定します。trueを設定すると、出力されるイメージのサイズが入力されたイメージと同じになるように
298
+ ゼロパディングを行います。
295
299
  * Float weight_decay: 0
296
300
  重み減衰を行うL2正則化項の強さを設定します。
297
301
 
@@ -300,17 +304,18 @@ nilを指定すると、RandomNormalイニシャライザーが使用されま
300
304
  maxプーリングを行うレイヤーです。
301
305
 
302
306
  ## 【Instance methods】
303
- ## def initialize(pool_height, pool_width, strides: nil, padding: 0)
307
+ ## def initialize(pool_width, pool_height, strides: nil, padding: false)
304
308
  コンストラクタ。
305
309
  ### arguments
306
- * Integer pool_height
307
- プーリングを行う縦の長さ。
308
310
  * Integer pool_width
309
311
  プーリングを行う横の長さ。
312
+ * Integer pool_height
313
+ プーリングを行う縦の長さ。
310
314
  * Array<Integer> strides: nil
311
- 畳み込みを行う際のストライドの単位を指定します。配列の要素0でy軸方向のストライドを設定し、要素1でx軸方向のストライドを設定します。なお、nilが設定された場合は、[pool_height, pool_width]がstridesの値となります。
312
- * Integer padding: 0
313
- イメージに対してゼロパディングを行う単位を指定します。
315
+ 畳み込みを行う際のストライドの単位を指定します。配列の要素0でy軸方向のストライドを設定し、要素1でx軸方向のストライドを設定します。なお、nilが設定された場合は、[pool_width, pool_height]がstridesの値となります。
316
+ * bool padding: true
317
+ イメージに対してゼロパディングを行うか否かを設定します。trueを設定すると、出力されるイメージのサイズが入力されたイメージと同じになるように
318
+ ゼロパディングを行います。
314
319
 
315
320
 
316
321
  # class Flatten
@@ -54,7 +54,7 @@ CIFAR-10を扱うモジュールです。
54
54
  Array
55
55
  [イメージデータ, ラベルデータ]の形式で取得します。
56
56
  * イメージデータ
57
- UInt8の[50000, 32, 32, 3]の形式
57
+ UInt8の[50000, 3, 32, 32]の形式
58
58
  * テストデータ
59
59
  UInt8の[50000]の形式
60
60
 
@@ -66,7 +66,7 @@ Array
66
66
  Array
67
67
  [イメージデータ, ラベルデータ]の形式で取得します。
68
68
  * イメージデータ
69
- UInt8の[10000, 32, 32, 3]の形式
69
+ UInt8の[10000, 3, 32, 32]の形式
70
70
  * テストデータ
71
71
  UInt8の[10000]の形式
72
72
 
@@ -12,8 +12,8 @@ CIFAR10 = DNN::CIFAR10
12
12
  x_train, y_train = CIFAR10.load_train
13
13
  x_test, y_test = CIFAR10.load_test
14
14
 
15
- x_train = SFloat.cast(x_train).reshape(x_train.shape[0], 32, 32, 3).transpose(0, 3, 2, 1)
16
- x_test = SFloat.cast(x_test).reshape(x_test.shape[0], 32, 32, 3).transpose(0, 3, 2, 1)
15
+ x_train = SFloat.cast(x_train).transpose(0, 2, 3, 1)
16
+ x_test = SFloat.cast(x_test).transpose(0, 2, 3, 1)
17
17
 
18
18
  x_train /= 255
19
19
  x_test /= 255
@@ -23,23 +23,23 @@ y_test = DNN::Util.to_categorical(y_test, 10)
23
23
 
24
24
  model = Model.new
25
25
 
26
- model << InputLayer.new([3, 32, 32])
26
+ model << InputLayer.new([32, 32, 3])
27
27
 
28
- model << Conv2D.new(16, 5, 5)
28
+ model << Conv2D.new(16, 5, 5, padding: true)
29
29
  model << BatchNormalization.new
30
30
  model << ReLU.new
31
31
 
32
- model << Conv2D.new(16, 5, 5)
32
+ model << Conv2D.new(16, 5, 5, padding: true)
33
33
  model << BatchNormalization.new
34
34
  model << ReLU.new
35
35
 
36
36
  model << MaxPool2D.new(2, 2)
37
37
 
38
- model << Conv2D.new(32, 5, 5)
38
+ model << Conv2D.new(32, 5, 5, padding: true)
39
39
  model << BatchNormalization.new
40
40
  model << ReLU.new
41
41
 
42
- model << Conv2D.new(32, 5, 5)
42
+ model << Conv2D.new(32, 5, 5, padding: true)
43
43
  model << BatchNormalization.new
44
44
  model << ReLU.new
45
45
 
@@ -55,8 +55,4 @@ model << SoftmaxWithLoss.new
55
55
 
56
56
  model.compile(Adam.new)
57
57
 
58
- model.train(x_train, y_train, 20, batch_size: 100) do
59
- model.test(x_test, y_test)
60
- end
61
-
62
- model.save("trained_cifar10.marshal")
58
+ model.train(x_train, y_train, 10, batch_size: 100, test: [x_test, y_test])
@@ -38,6 +38,4 @@ model << SoftmaxWithLoss.new
38
38
 
39
39
  model.compile(RMSProp.new)
40
40
 
41
- model.train(x_train, y_train, 10, batch_size: 100) do
42
- model.test(x_test, y_test)
43
- end
41
+ model.train(x_train, y_train, 10, batch_size: 100, test: [x_test, y_test])
@@ -12,8 +12,8 @@ MNIST = DNN::MNIST
12
12
  x_train, y_train = MNIST.load_train
13
13
  x_test, y_test = MNIST.load_test
14
14
 
15
- x_train = SFloat.cast(x_train).reshape(x_train.shape[0], 1, 28, 28)
16
- x_test = SFloat.cast(x_test).reshape(x_test.shape[0], 1, 28, 28)
15
+ x_train = SFloat.cast(x_train).reshape(x_train.shape[0], 28, 28, 1)
16
+ x_test = SFloat.cast(x_test).reshape(x_test.shape[0], 28, 28, 1)
17
17
 
18
18
  x_train /= 255
19
19
  x_test /= 255
@@ -23,7 +23,7 @@ y_test = DNN::Util.to_categorical(y_test, 10)
23
23
 
24
24
  model = Model.new
25
25
 
26
- model << InputLayer.new([1, 28, 28])
26
+ model << InputLayer.new([28, 28, 1])
27
27
 
28
28
  model << Conv2D.new(16, 5, 5)
29
29
  model << BatchNormalization.new
@@ -47,6 +47,4 @@ model << SoftmaxWithLoss.new
47
47
 
48
48
  model.compile(Adam.new)
49
49
 
50
- model.train(x_train, y_train, 10, batch_size: 100) do
51
- model.test(x_test, y_test)
52
- end
50
+ model.train(x_train, y_train, 10, batch_size: 100, test: [x_test, y_test])
@@ -5,11 +5,20 @@ module DNN
5
5
  class Layer
6
6
  include Numo
7
7
 
8
+ def initialize
9
+ @builded = false
10
+ end
11
+
8
12
  #Initialize layer when model is compiled.
9
- def init(model)
13
+ def build(model)
14
+ @builded = true
10
15
  @model = model
11
16
  end
12
17
 
18
+ def builded?
19
+ @builded
20
+ end
21
+
13
22
  #Forward propagation.
14
23
  def forward() end
15
24
 
@@ -42,7 +51,7 @@ module DNN
42
51
  @grads = {}
43
52
  end
44
53
 
45
- def init(model)
54
+ def build(model)
46
55
  super
47
56
  init_params
48
57
  end
@@ -153,46 +162,58 @@ module DNN
153
162
  module Convert
154
163
  private
155
164
 
156
- def im2col(img, out_h, out_w, fh, fw, strides)
157
- bs, fn = img.shape[0..1]
158
- col = SFloat.zeros(bs, fn, fh, fw, out_h, out_w)
159
- (0...fh).each do |i|
160
- i_range = (i...(i + strides[0] * out_h)).step(strides[0]).to_a
161
- (0...fw).each do |j|
162
- j_range = (j...(j + strides[1] * out_w)).step(strides[1]).to_a
163
- col[true, true, i, j, true, true] = img[true, true, i_range, j_range]
165
+ def im2col(img, out_w, out_h, fil_w, fil_h, strides)
166
+ bsize = img.shape[0]
167
+ ch = img.shape[3]
168
+ col = SFloat.zeros(bsize, ch, fil_w, fil_h, out_w, out_h)
169
+ img = img.transpose(0, 3, 1, 2)
170
+ (0...fil_h).each do |i|
171
+ i_range = (i...(i + strides[1] * out_h)).step(strides[1]).to_a
172
+ (0...fil_w).each do |j|
173
+ j_range = (j...(j + strides[0] * out_w)).step(strides[0]).to_a
174
+ col[true, true, j, i, true, true] = img[true, true, j_range, i_range]
164
175
  end
165
176
  end
166
- col.transpose(0, 4, 5, 1, 2, 3).reshape(bs * out_h * out_w, fn * fh * fw)
177
+ col.transpose(0, 4, 5, 2, 3, 1).reshape(bsize * out_w * out_h, fil_w * fil_h * ch)
167
178
  end
168
-
169
- def col2im(col, img_shape, out_h, out_w, fh, fw, strides)
170
- bs, fn, ih, iw = img_shape
171
- col = col.reshape(bs, out_h, out_w, fn, fh, fw).transpose(0, 3, 4, 5, 1, 2)
172
- img = SFloat.zeros(bs, fn, ih, iw)
173
- (0...fh).each do |i|
174
- i_range = (i...(i + strides[0] * out_h)).step(strides[0]).to_a
175
- (0...fw).each do |j|
176
- j_range = (j...(j + strides[1] * out_w)).step(strides[1]).to_a
177
- img[true, true, i_range, j_range] += col[true, true, i, j, true, true]
179
+
180
+ def col2im(col, img_shape, out_w, out_h, fil_w, fil_h, strides)
181
+ bsize, img_w, img_h, ch = img_shape
182
+ col = col.reshape(bsize, out_w, out_h, fil_w, fil_h, ch).transpose(0, 5, 3, 4, 1, 2)
183
+ img = SFloat.zeros(bsize, ch, img_w, img_h)
184
+ (0...fil_h).each do |i|
185
+ i_range = (i...(i + strides[1] * out_h)).step(strides[1]).to_a
186
+ (0...fil_w).each do |j|
187
+ j_range = (j...(j + strides[0] * out_w)).step(strides[0]).to_a
188
+ img[true, true, j_range, i_range] += col[true, true, j, i, true, true]
178
189
  end
179
190
  end
180
- img
191
+ img.transpose(0, 2, 3, 1)
181
192
  end
182
193
 
183
194
  def padding(img, pad)
184
- bs, c, ih, iw = img.shape
185
- ih2 = ih + pad * 2
186
- iw2 = iw + pad * 2
187
- img2 = SFloat.zeros(bs, c, ih2, iw2)
188
- img2[true, true, pad...(ih + pad), pad...(iw + pad)] = img
195
+ bsize, img_w, img_h, ch = img.shape
196
+ img2 = SFloat.zeros(bsize, img_w + pad[0], img_h + pad[1], ch)
197
+ i_begin = pad[1] / 2
198
+ i_end = i_begin + img_h
199
+ j_begin = pad[0] / 2
200
+ j_end = j_begin + img_w
201
+ img2[true, j_begin...j_end, i_begin...i_end, true] = img
189
202
  img2
190
203
  end
191
204
 
192
205
  def back_padding(img, pad)
193
- i_end = img.shape[2] - pad
194
- j_end = img.shape[3] - pad
195
- img[true, true, pad...i_end, pad...j_end]
206
+ i_begin = pad[1] / 2
207
+ i_end = img.shape[2] - (pad[1] / 2.0).round
208
+ j_begin = pad[0] / 2
209
+ j_end = img.shape[1] - (pad[0] / 2.0).round
210
+ img[true, j_begin...j_end, i_begin...i_end, true]
211
+ end
212
+
213
+ def out_size(prev_w, prev_h, fil_w, fil_h, strides)
214
+ out_w = (prev_w - fil_w) / strides[1] + 1
215
+ out_h = (prev_h - fil_h) / strides[0] + 1
216
+ [out_w, out_h]
196
217
  end
197
218
  end
198
219
 
@@ -201,16 +222,16 @@ module DNN
201
222
  include Initializers
202
223
  include Convert
203
224
 
204
- def initialize(num_filters, filter_height, filter_width,
225
+ def initialize(num_filters, filter_width, filter_height,
205
226
  weight_initializer: nil,
206
227
  bias_initializer: nil,
207
228
  strides: [1, 1],
208
- padding: 0,
229
+ padding: false,
209
230
  weight_decay: 0)
210
231
  super()
211
232
  @num_filters = num_filters
212
- @filter_height = filter_height
213
233
  @filter_width = filter_width
234
+ @filter_height = filter_height
214
235
  @weight_initializer = (weight_initializer || RandomNormal.new)
215
236
  @bias_initializer = (bias_initializer || Zeros.new)
216
237
  @strides = strides
@@ -219,31 +240,34 @@ module DNN
219
240
  end
220
241
 
221
242
  def self.load_hash(hash)
222
- Conv2D.new(hash[:num_filters], hash[:filter_height], hash[:filter_width],
243
+ Conv2D.new(hash[:num_filters], hash[:filter_width], hash[:filter_height],
223
244
  weight_initializer: Util.load_hash(hash[:weight_initializer]),
224
245
  bias_initializer: Util.load_hash(hash[:bias_initializer]),
225
246
  strides: hash[:strides],
226
247
  padding: hash[:padding],
227
248
  weight_decay: hash[:weight_decay])
228
249
  end
229
-
230
- def init(model)
250
+
251
+ def build(model)
231
252
  super
232
- prev_height, prev_width = prev_layer.shape[1], prev_layer.shape[2]
233
- @out_height = (prev_height + @padding * 2 - @filter_height) / @strides[0] + 1
234
- @out_width = (prev_width + @padding * 2 - @filter_width) / @strides[1] + 1
253
+ prev_width, prev_height = prev_layer.shape[0..1]
254
+ @out_width, @out_height = out_size(prev_width, prev_height, @filter_width, @filter_height, @strides)
255
+ if @padding
256
+ @pad = [prev_width - @out_width, prev_height - @out_height]
257
+ @out_width = prev_width
258
+ @out_height = prev_height
259
+ end
235
260
  end
236
-
261
+
237
262
  def forward(x)
238
- x = padding(x, 2) if @padding > 0
263
+ x = padding(x, @pad) if @padding
239
264
  @x_shape = x.shape
240
- @col = im2col(x, @out_height, @out_width, @filter_height, @filter_width, @strides)
265
+ @col = im2col(x, @out_width, @out_height, @filter_width, @filter_height, @strides)
241
266
  out = @col.dot(@params[:weight])
242
- out.reshape(@model.batch_size, @out_height, @out_width, out.shape[3]).transpose(0, 3, 1, 2)
267
+ out.reshape(x.shape[0], @out_width, @out_height, out.shape[3])
243
268
  end
244
-
269
+
245
270
  def backward(dout)
246
- dout = dout.transpose(0, 2, 3, 1)
247
271
  dout = dout.reshape(dout.shape[0..2].reduce(:*), dout.shape[3])
248
272
  @grads[:weight] = @col.transpose.dot(dout)
249
273
  if @weight_decay > 0
@@ -252,20 +276,20 @@ module DNN
252
276
  end
253
277
  @grads[:bias] = dout.sum(0)
254
278
  dcol = dout.dot(@params[:weight].transpose)
255
- dx = col2im(dcol, @x_shape, @out_height, @out_width, @filter_height, @filter_width, @strides)
256
- @padding ? back_padding(dx, @padding) : dx
279
+ dx = col2im(dcol, @x_shape, @out_width, @out_height, @filter_width, @filter_height, @strides)
280
+ @padding ? back_padding(dx, @pad) : dx
257
281
  end
258
-
282
+
259
283
  def shape
260
- [@num_filters, @out_height, @out_width]
284
+ [@out_width, @out_height, @num_filters]
261
285
  end
262
286
 
263
287
  def to_hash
264
288
  {
265
289
  name: self.class.name,
266
290
  num_filters: @num_filters,
267
- filter_height: @filter_height,
268
291
  filter_width: @filter_width,
292
+ filter_height: @filter_height,
269
293
  weight_initializer: @weight_initializer.to_hash,
270
294
  bias_initializer: @bias_initializer.to_hash,
271
295
  strides: @strides,
@@ -277,8 +301,8 @@ module DNN
277
301
  private
278
302
 
279
303
  def init_params
280
- num_prev_filter = prev_layer.shape[0]
281
- @params[:weight] = SFloat.new(num_prev_filter * @filter_height * @filter_height, @num_filters)
304
+ num_prev_filter = prev_layer.shape[2]
305
+ @params[:weight] = SFloat.new(num_prev_filter * @filter_width * @filter_height, @num_filters)
282
306
  @params[:bias] = SFloat.new(@num_filters)
283
307
  @weight_initializer.init_param(self, :weight)
284
308
  @bias_initializer.init_param(self, :bias)
@@ -289,49 +313,52 @@ module DNN
289
313
  class MaxPool2D < Layer
290
314
  include Convert
291
315
 
292
- def initialize(pool_height, pool_width, strides: nil, padding: 0)
293
- @pool_height = pool_height
316
+ def initialize(pool_width, pool_height, strides: nil, padding: false)
294
317
  @pool_width = pool_width
295
- @strides = strides ? strides : [@pool_height, @pool_width]
318
+ @pool_height = pool_height
319
+ @strides = strides ? strides : [@pool_width, @pool_height]
296
320
  @padding = padding
297
- end
298
-
299
- def init(model)
321
+ end
322
+
323
+ def build(model)
300
324
  super
301
- prev_height, prev_width = prev_layer.shape[1], prev_layer.shape[2]
302
- @num_channel = prev_layer.shape[0]
303
- @out_height = (prev_height + @padding * 2 - @pool_height) / @strides[0] + 1
304
- @out_width = (prev_width + @padding * 2 - @pool_width) / @strides[1] + 1
325
+ prev_width, prev_height = prev_layer.shape[0..1]
326
+ @num_channel = prev_layer.shape[2]
327
+ @out_width, @out_height = out_size(prev_width, prev_height, @pool_width, @pool_height, @strides)
328
+ if @padding
329
+ @pad = [prev_width - @out_width, prev_height - @out_height]
330
+ @out_width = prev_width
331
+ @out_height = prev_height
332
+ end
305
333
  end
306
-
334
+
307
335
  def forward(x)
308
- x = padding(x, 2) if @padding > 0
336
+ x = padding(x, @pad) if @padding
309
337
  @x_shape = x.shape
310
- col = im2col(x, @out_height, @out_width, @pool_height, @pool_width, @strides)
311
- col = col.reshape(x.shape[0] * @out_height * @out_width * x.shape[1], @pool_height * @pool_width)
338
+ col = im2col(x, @out_width, @out_height, @pool_width, @pool_height, @strides)
339
+ col = col.reshape(x.shape[0] * @out_width * @out_height * x.shape[3], @pool_width * @pool_height)
312
340
  @max_index = col.max_index(1)
313
- col.max(1).reshape(x.shape[0], @out_height, @out_width, x.shape[1]).transpose(0, 3, 1, 2)
341
+ col.max(1).reshape(x.shape[0], @out_width, @out_height, x.shape[3])#.transpose(0, 3, 1, 2)
314
342
  end
315
-
343
+
316
344
  def backward(dout)
317
- dout = dout.transpose(0, 2, 3, 1)
318
- pool_size = @pool_height * @pool_width
345
+ pool_size = @pool_width * @pool_height
319
346
  dmax = SFloat.zeros(dout.size * pool_size)
320
347
  dmax[@max_index] = dout.flatten
321
348
  dcol = dmax.reshape(dout.shape[0..2].reduce(:*), dout.shape[3] * pool_size)
322
- dx = col2im(dcol, @x_shape, @out_height, @out_width, @pool_height, @pool_width, @strides)
323
- @padding ? back_padding(dx, @padding) : dx
349
+ dx = col2im(dcol, @x_shape, @out_width, @out_height, @pool_width, @pool_height, @strides)
350
+ @padding ? back_padding(dx, @pad) : dx
324
351
  end
325
-
352
+
326
353
  def shape
327
- [@num_channel, @out_height, @out_width]
354
+ [@out_width, @out_height, @num_channel]
328
355
  end
329
356
 
330
357
  def to_hash
331
358
  {
332
359
  name: self.class.name,
333
- pool_height: @pool_height,
334
360
  pool_width: @pool_width,
361
+ pool_height: @pool_height,
335
362
  strides: @strides,
336
363
  padding: @padding,
337
364
  }
@@ -14,6 +14,7 @@ module DNN
14
14
  @layers = []
15
15
  @optimizer = nil
16
16
  @batch_size = nil
17
+ @compiled = false
17
18
  end
18
19
 
19
20
  def self.load(file_name)
@@ -73,18 +74,24 @@ module DNN
73
74
  unless optimizer.is_a?(Optimizers::Optimizer)
74
75
  raise DNN_TypeError.new("optimizer is not an instance of the DNN::Optimizers::Optimizer class.")
75
76
  end
77
+ @compiled = true
76
78
  layers_check
77
79
  @optimizer = optimizer
78
80
  @layers.each do |layer|
79
- layer.init(self)
81
+ layer.build(self)
80
82
  end
81
83
  layers_shape_check
82
84
  end
85
+
86
+ def compiled?
87
+ @compiled
88
+ end
83
89
 
84
90
  def train(x, y, epochs,
85
91
  batch_size: 1,
86
- batch_proc: nil,
92
+ test: nil,
87
93
  verbose: true,
94
+ batch_proc: nil,
88
95
  &epoch_proc)
89
96
  @batch_size = batch_size
90
97
  num_train_data = x.shape[0]
@@ -110,6 +117,10 @@ module DNN
110
117
  log << " #{num_trained_data}/#{num_train_data} loss: #{loss}"
111
118
  print log if verbose
112
119
  end
120
+ if verbose && test
121
+ acc = accurate(test[0], test[1], batch_size,&batch_proc)
122
+ print " accurate: #{acc}"
123
+ end
113
124
  puts "" if verbose
114
125
  epoch_proc.call(epoch) if epoch_proc
115
126
  end
@@ -124,13 +135,6 @@ module DNN
124
135
  @layers[-1].loss(y)
125
136
  end
126
137
 
127
- def test(x, y, batch_size = nil, &batch_proc)
128
- @batch_size = batch_size if batch_size
129
- acc = accurate(x, y, @batch_size, &batch_proc)
130
- puts "accurate: #{acc}"
131
- acc
132
- end
133
-
134
138
  def accurate(x, y, batch_size = nil, &batch_proc)
135
139
  @batch_size = batch_size if batch_size
136
140
  correct = 0
@@ -156,8 +160,6 @@ module DNN
156
160
  forward(x, false)
157
161
  end
158
162
 
159
- private
160
-
161
163
  def forward(x, training)
162
164
  @training = training
163
165
  @layers.each do |layer|
@@ -171,8 +173,11 @@ module DNN
171
173
  @layers[0..-1].reverse.each do |layer|
172
174
  dout = layer.backward(dout)
173
175
  end
176
+ dout
174
177
  end
175
178
 
179
+ private
180
+
176
181
  def layers_check
177
182
  unless @layers.first.is_a?(Layers::InputLayer)
178
183
  raise DNN_Error.new("The first layer is not an InputLayer.")
@@ -1,3 +1,3 @@
1
1
  module DNN
2
- VERSION = "0.1.8"
2
+ VERSION = "0.2.0"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruby-dnn
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.8
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - unagiootoro