ruby-dnn 0.9.3 → 0.9.4

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: 2926b7a9ca6f3766ee46bba676837bd4cb98d55220ba37e4ef12109ef244a444
4
- data.tar.gz: 57db99b2200d3d0946c6bb886f97308f6b09e550f5ab66802f61a7d13bd9b8fe
3
+ metadata.gz: 670d2d681e3929d20c5c855c97dbad1759560eb45018d20a9089c6bc62787833
4
+ data.tar.gz: a9360305a97aed1bac5300010fbfa3ea85565eab70d299a9d07acd78d6b24d63
5
5
  SHA512:
6
- metadata.gz: 19c77e661bb6b208c279ce80d85a115572e124bad50dd97f3c8ec1097aa1bf4f245cef11bee58c3ffbc5c8c3a47f4aa6e9e3bb89db0ba8c1c7ff6ef2066764b3
7
- data.tar.gz: 9a8d414fcb3401b883bde2cd54f6b320817ee66389e43971f62a4d9f85e1888db36842d704c3c2392f27f68266f6dcd8faf207723dfca25195651966e3713652
6
+ metadata.gz: d1f3b49241a81bf8c56b595bdaeecf097d8d3d461a02ab37860c6d2eede4a7e5cfadd0f83d2759cdbbdc7d541bbadebd11e55a80b0cfce9c2bd8a216efb2596d
7
+ data.tar.gz: e92230987d4c59cf4395ee33695fc974c65940f7df6aecfc3f89ebf65bbbeaae95433079df8855c448850923b5d230807eca2d2c135d475df6aa1aba6431988a
@@ -0,0 +1,33 @@
1
+ require "dnn"
2
+ require "dnn/lib/iris"
3
+ # require "numo/linalg/autoloader"
4
+
5
+ include DNN::Layers
6
+ include DNN::Activations
7
+ include DNN::Optimizers
8
+ include DNN::Losses
9
+ Model = DNN::Model
10
+ Iris = DNN::Iris
11
+
12
+ x, y = Iris.load(true)
13
+ x_train, y_train = x[0...100, true], y[0...100]
14
+ x_test, y_test = x[100...150, true], y[100...150]
15
+
16
+ x_train /= 255
17
+ x_test /= 255
18
+
19
+ y_train = DNN::Utils.to_categorical(y_train, 3, Numo::SFloat)
20
+ y_test = DNN::Utils.to_categorical(y_test, 3, Numo::SFloat)
21
+
22
+ model = Model.new
23
+
24
+ model << InputLayer.new(4)
25
+
26
+ model << Dense.new(64)
27
+ model << ReLU.new
28
+
29
+ model << Dense.new(3)
30
+
31
+ model.compile(Adam.new, SoftmaxCrossEntropy.new)
32
+
33
+ model.train(x_train, y_train, 1000, batch_size: 10, test: [x_test, y_test])
@@ -3,7 +3,7 @@ module DNN
3
3
 
4
4
  class Sigmoid < Layers::Layer
5
5
  def forward(x)
6
- @out = Utils.sigmoid(x)
6
+ @out = 1 / (1 + NMath.exp(-x))
7
7
  end
8
8
 
9
9
  def backward(dout)
@@ -4,7 +4,7 @@ module DNN
4
4
  module Conv2DModule
5
5
  private
6
6
 
7
- # img[bsize, out_h, out_w, channel] to col[bsize * out_h * out_w, fil_h * fil_w * ch]
7
+ # img[bsize, out_h, out_w, ch] to col[bsize * out_h * out_w, fil_h * fil_w * ch]
8
8
  def im2col(img, out_h, out_w, fil_h, fil_w, strides)
9
9
  bsize = img.shape[0]
10
10
  ch = img.shape[3]
@@ -19,7 +19,7 @@ module DNN
19
19
  col.reshape(bsize * out_h * out_w, fil_h * fil_w * ch)
20
20
  end
21
21
 
22
- # col[bsize * out_h * out_w, fil_h * fil_w * ch] to img[bsize, out_h, out_w, channel]
22
+ # col[bsize * out_h * out_w, fil_h * fil_w * ch] to img[bsize, out_h, out_w, ch]
23
23
  def col2im(col, img_shape, out_h, out_w, fil_h, fil_w, strides)
24
24
  bsize, img_h, img_w, ch = img_shape
25
25
  col = col.reshape(bsize, out_h, out_w, fil_h, fil_w, ch)
@@ -84,7 +84,8 @@ module DNN
84
84
  strides: hash[:strides],
85
85
  padding: hash[:padding],
86
86
  l1_lambda: hash[:l1_lambda],
87
- l2_lambda: hash[:l2_lambda])
87
+ l2_lambda: hash[:l2_lambda],
88
+ use_bias: hash[:use_bias])
88
89
  end
89
90
 
90
91
  # @param [Integer] num_filters number of filters.
@@ -93,13 +94,14 @@ module DNN
93
94
  # @param [Bool] padding Whether to padding.
94
95
  def initialize(num_filters, filter_size,
95
96
  weight_initializer: Initializers::RandomNormal.new,
96
- bias_initializer: Initializers::RandomNormal.new,
97
+ bias_initializer: Initializers::Zeros.new,
97
98
  strides: 1,
98
99
  padding: false,
99
100
  l1_lambda: 0,
100
- l2_lambda: 0)
101
+ l2_lambda: 0,
102
+ use_bias: true)
101
103
  super(weight_initializer: weight_initializer, bias_initializer: bias_initializer,
102
- l1_lambda: l1_lambda, l2_lambda: l2_lambda)
104
+ l1_lambda: l1_lambda, l2_lambda: l2_lambda, use_bias: use_bias)
103
105
  @num_filters = num_filters
104
106
  @filter_size = filter_size.is_a?(Integer) ? [filter_size, filter_size] : filter_size
105
107
  @strides = strides.is_a?(Integer) ? [strides, strides] : strides
@@ -120,14 +122,15 @@ module DNN
120
122
  x = padding(x, @pad_size) if @padding
121
123
  @x_shape = x.shape
122
124
  @col = im2col(x, *@out_size, *@filter_size, @strides)
123
- out = @col.dot(@weight.data) + @bias.data
125
+ out = @col.dot(@weight.data)
126
+ out += @bias.data if @bias
124
127
  out.reshape(x.shape[0], *@out_size, out.shape[3])
125
128
  end
126
129
 
127
130
  def backward(dout)
128
131
  dout = dout.reshape(dout.shape[0..2].reduce(:*), dout.shape[3])
129
132
  @weight.grad = @col.transpose.dot(dout)
130
- @bias.grad = dout.sum(0)
133
+ @bias.grad = dout.sum(0) if @bias
131
134
  dcol = dout.dot(@weight.data.transpose)
132
135
  dx = col2im(dcol, @x_shape, *@out_size, *@filter_size, @strides)
133
136
  @padding ? back_padding(dx, @pad_size) : dx
@@ -166,7 +169,7 @@ module DNN
166
169
  def init_params
167
170
  num_prev_filter = @input_shape[2]
168
171
  @weight.data = Xumo::SFloat.new(@filter_size.reduce(:*) * num_prev_filter, @num_filters)
169
- @bias.data = Xumo::SFloat.new(@num_filters)
172
+ @bias.data = Xumo::SFloat.new(@num_filters) if @bias
170
173
  super()
171
174
  end
172
175
  end
@@ -305,18 +308,26 @@ module DNN
305
308
  @num_channel = input_shape[2]
306
309
  end
307
310
 
311
+ include Conv2DModule
312
+
308
313
  def forward(x)
309
314
  @x_shape = x.shape
310
315
  unpool_h, unpool_w = @unpool_size
311
316
  x2 = Xumo::SFloat.zeros(x.shape[0], x.shape[1], unpool_h, x.shape[2], unpool_w, @num_channel)
312
- x2[true, true, 0, true, 0, true] = x
317
+ unpool_h.times do |i|
318
+ unpool_w.times do |j|
319
+ x2[true, true, i, true, j, true] = x
320
+ end
321
+ end
313
322
  x2.reshape(x.shape[0], *@out_size, x.shape[3])
314
323
  end
315
324
 
316
325
  def backward(dout)
317
- unpool_h, unpool_w = @unpool_size
318
- dout = dout.reshape(dout.shape[0], @x_shape[1], unpool_h, @x_shape[2], unpool_w, @num_channel)
319
- dout[true, true, 0, true, 0, true].clone
326
+ in_size = input_shape[0..1]
327
+ col = im2col(dout, *input_shape[0..1], *@unpool_size, @unpool_size)
328
+ col = col.reshape(dout.shape[0] * in_size.reduce(:*), @unpool_size.reduce(:*), dout.shape[3]).transpose(0, 2, 1)
329
+ .reshape(dout.shape[0] * in_size.reduce(:*) * dout.shape[3], @unpool_size.reduce(:*))
330
+ col.sum(1).reshape(dout.shape[0], *in_size, dout.shape[3])
320
331
  end
321
332
 
322
333
  def output_shape
@@ -113,26 +113,34 @@ module DNN
113
113
  attr_reader :weight_initializer
114
114
  # @return [DNN::Initializers] bias initializer.
115
115
  attr_reader :bias_initializer
116
- # @return [Float] L1 regularization
116
+ # @return [Float] L1 regularization.
117
117
  attr_reader :l1_lambda
118
- # @return [Float] L2 regularization
118
+ # @return [Float] L2 regularization.
119
119
  attr_reader :l2_lambda
120
120
 
121
121
  # @param [DNN::Initializers] weight_initializer weight initializer.
122
122
  # @param [DNN::Initializers] bias_initializer bias initializer.
123
123
  # @param [Float] l1_lambda L1 regularization
124
124
  # @param [Float] l2_lambda L2 regularization
125
+ # @param [Bool] use_bias whether to use bias.
125
126
  def initialize(weight_initializer: Initializers::RandomNormal.new,
126
127
  bias_initializer: Initializers::Zeros.new,
127
128
  l1_lambda: 0,
128
- l2_lambda: 0)
129
+ l2_lambda: 0,
130
+ use_bias: true)
129
131
  super()
130
132
  @weight_initializer = weight_initializer
131
133
  @bias_initializer = bias_initializer
132
134
  @l1_lambda = l1_lambda
133
135
  @l2_lambda = l2_lambda
134
136
  @params[:weight] = @weight = Param.new
135
- @params[:bias] = @bias = Param.new
137
+ # For compatibility on or before with v0.9.3, setting use_bias to nil use bias.
138
+ # Therefore, setting use_bias to nil is deprecated.
139
+ if use_bias || use_bias == nil
140
+ @params[:bias] = @bias = Param.new
141
+ else
142
+ @params[:bias] = @bias = nil
143
+ end
136
144
  end
137
145
 
138
146
  def regularizers
@@ -142,6 +150,11 @@ module DNN
142
150
  regularizers
143
151
  end
144
152
 
153
+ # @return [Bool] Return whether to use bias.
154
+ def use_bias
155
+ @bias ? true : false
156
+ end
157
+
145
158
  def to_hash(merge_hash)
146
159
  super({weight_initializer: @weight_initializer.to_hash,
147
160
  bias_initializer: @bias_initializer.to_hash,
@@ -153,7 +166,7 @@ module DNN
153
166
 
154
167
  def init_params
155
168
  @weight_initializer.init_param(self, @weight)
156
- @bias_initializer.init_param(self, @bias)
169
+ @bias_initializer.init_param(self, @bias) if @bias
157
170
  end
158
171
  end
159
172
 
@@ -168,7 +181,8 @@ module DNN
168
181
  weight_initializer: Utils.load_hash(hash[:weight_initializer]),
169
182
  bias_initializer: Utils.load_hash(hash[:bias_initializer]),
170
183
  l1_lambda: hash[:l1_lambda],
171
- l2_lambda: hash[:l2_lambda])
184
+ l2_lambda: hash[:l2_lambda],
185
+ use_bias: hash[:use_bias])
172
186
  end
173
187
 
174
188
  # @param [Integer] num_nodes number of nodes.
@@ -176,20 +190,23 @@ module DNN
176
190
  weight_initializer: Initializers::RandomNormal.new,
177
191
  bias_initializer: Initializers::Zeros.new,
178
192
  l1_lambda: 0,
179
- l2_lambda: 0)
193
+ l2_lambda: 0,
194
+ use_bias: true)
180
195
  super(weight_initializer: weight_initializer, bias_initializer: bias_initializer,
181
- l1_lambda: l1_lambda, l2_lambda: l2_lambda)
196
+ l1_lambda: l1_lambda, l2_lambda: l2_lambda, use_bias: use_bias)
182
197
  @num_nodes = num_nodes
183
198
  end
184
199
 
185
200
  def forward(x)
186
201
  @x = x
187
- @x.dot(@weight.data) + @bias.data
202
+ out = x.dot(@weight.data)
203
+ out += @bias.data if @bias
204
+ out
188
205
  end
189
206
 
190
207
  def backward(dout)
191
208
  @weight.grad = @x.transpose.dot(dout)
192
- @bias.grad = dout.sum(0)
209
+ @bias.grad = dout.sum(0) if @bias
193
210
  dout.dot(@weight.data.transpose)
194
211
  end
195
212
 
@@ -208,7 +225,7 @@ module DNN
208
225
  def init_params
209
226
  num_prev_nodes = @input_shape[0]
210
227
  @weight.data = Xumo::SFloat.new(num_prev_nodes, @num_nodes)
211
- @bias.data = Xumo::SFloat.new(@num_nodes)
228
+ @bias.data = Xumo::SFloat.new(@num_nodes) if @bias
212
229
  super()
213
230
  end
214
231
  end
@@ -100,8 +100,12 @@ module DNN
100
100
 
101
101
 
102
102
  class SoftmaxCrossEntropy < Loss
103
+ def self.softmax(x)
104
+ NMath.exp(x) / NMath.exp(x).sum(1).reshape(x.shape[0], 1)
105
+ end
106
+
103
107
  def loss(x, y)
104
- @out = Utils.softmax(x)
108
+ @out = SoftmaxCrossEntropy.softmax(x)
105
109
  batch_size = y.shape[0]
106
110
  -(y * NMath.log(@out + 1e-7)).sum / batch_size
107
111
  end
@@ -113,10 +117,14 @@ module DNN
113
117
 
114
118
 
115
119
  class SigmoidCrossEntropy < Loss
120
+ def initialize
121
+ @sigmoid = Sigmoid.new
122
+ end
123
+
116
124
  def loss(x, y)
117
- @out = Utils.sigmoid(x)
125
+ @out = @sigmoid.forward(x)
118
126
  batch_size = y.shape[0]
119
- -(y * NMath.log(@out + 1e-7) + (1 - y) * NMath.log(1 - @out + 1e-7)).sum / batch_size
127
+ -(y * NMath.log(@out + 1e-7) + (1 - y) * NMath.log(1 - @out + 1e-7))
120
128
  end
121
129
 
122
130
  def backward(y)
@@ -213,8 +213,10 @@ module DNN
213
213
  puts "【 epoch #{epoch}/#{epochs} 】" if verbose
214
214
  (num_train_datas.to_f / batch_size).ceil.times do |index|
215
215
  x_batch, y_batch = dataset.get_batch(batch_size)
216
- loss = train_on_batch(x_batch, y_batch, &batch_proc)
217
- if loss.nan?
216
+ loss_value = train_on_batch(x_batch, y_batch, &batch_proc)
217
+ if loss_value.is_a?(Numo::SFloat)
218
+ loss_value = loss_value.mean
219
+ elsif loss_value.nan?
218
220
  puts "\nloss is nan" if verbose
219
221
  return
220
222
  end
@@ -230,7 +232,7 @@ module DNN
230
232
  log << "_"
231
233
  end
232
234
  end
233
- log << " #{num_trained_datas}/#{num_train_datas} loss: #{sprintf('%.8f', loss)}"
235
+ log << " #{num_trained_datas}/#{num_train_datas} loss: #{sprintf('%.8f', loss_value)}"
234
236
  print log if verbose
235
237
  end
236
238
  if verbose && test
@@ -246,6 +248,7 @@ module DNN
246
248
  # Compile the model before use this method.
247
249
  # @param [Numo::SFloat] x Input training data.
248
250
  # @param [Numo::SFloat] y Output training data.
251
+ # @return [Float | Numo::SFloat] Return loss value in the form of Float or Numo::SFloat.
249
252
  # @yield [x, y] batch_proc Set proc to process per batch.
250
253
  def train_on_batch(x, y, &batch_proc)
251
254
  raise DNN_Error.new("The model is not compiled.") unless compiled?
@@ -304,7 +307,7 @@ module DNN
304
307
  # @param [Numo::SFloat] x Input data. However, x is single data.
305
308
  def predict1(x)
306
309
  check_xy_type(x)
307
- predict(Xumo::SFloat.cast([x]))[0, false]
310
+ predict(x.reshape(1, *x.shape))[0, false]
308
311
  end
309
312
 
310
313
  # @return [DNN::Model] Copy this model.
@@ -333,7 +336,7 @@ module DNN
333
336
 
334
337
  # TODO
335
338
  # It is not good to write the Layer class name directly in the Model class. I will fix it later.
336
- def forward(x, learning_phase)01
339
+ def forward(x, learning_phase)
337
340
  @layers.each do |layer|
338
341
  x = if layer.is_a?(Layers::Dropout) || layer.is_a?(Layers::BatchNormalization) || layer.is_a?(Model)
339
342
  layer.forward(x, learning_phase)
@@ -18,9 +18,10 @@ module DNN
18
18
  weight_initializer: RandomNormal.new,
19
19
  bias_initializer: Zeros.new,
20
20
  l1_lambda: 0,
21
- l2_lambda: 0)
21
+ l2_lambda: 0,
22
+ use_bias: true)
22
23
  super(weight_initializer: weight_initializer, bias_initializer: bias_initializer,
23
- l1_lambda: l1_lambda, l2_lambda: l2_lambda)
24
+ l1_lambda: l1_lambda, l2_lambda: l2_lambda, use_bias: use_bias)
24
25
  @num_nodes = num_nodes
25
26
  @stateful = stateful
26
27
  @return_sequences = return_sequences
@@ -47,7 +48,7 @@ module DNN
47
48
  def backward(dh2s)
48
49
  @weight.grad = Xumo::SFloat.zeros(*@weight.data.shape)
49
50
  @weight2.grad = Xumo::SFloat.zeros(*@weight2.data.shape)
50
- @bias.grad = Xumo::SFloat.zeros(*@bias.data.shape)
51
+ @bias.grad = Xumo::SFloat.zeros(*@bias.data.shape) if @bias
51
52
  unless @return_sequences
52
53
  dh = dh2s
53
54
  dh2s = Xumo::SFloat.zeros(dh.shape[0], @time_length, dh.shape[1])
@@ -114,7 +115,8 @@ module DNN
114
115
  def forward(x, h)
115
116
  @x = x
116
117
  @h = h
117
- h2 = x.dot(@weight.data) + h.dot(@weight2.data) + @bias.data
118
+ h2 = x.dot(@weight.data) + h.dot(@weight2.data)
119
+ h2 += @bias.data if @bias
118
120
  @activation.forward(h2)
119
121
  end
120
122
 
@@ -122,7 +124,7 @@ module DNN
122
124
  dh2 = @activation.backward(dh2)
123
125
  @weight.grad += @x.transpose.dot(dh2)
124
126
  @weight2.grad += @h.transpose.dot(dh2)
125
- @bias.grad += dh2.sum(0)
127
+ @bias.grad += dh2.sum(0) if @bias
126
128
  dx = dh2.dot(@weight.data.transpose)
127
129
  dh = dh2.dot(@weight2.data.transpose)
128
130
  [dx, dh]
@@ -143,7 +145,8 @@ module DNN
143
145
  weight_initializer: Utils.load_hash(hash[:weight_initializer]),
144
146
  bias_initializer: Utils.load_hash(hash[:bias_initializer]),
145
147
  l1_lambda: hash[:l1_lambda],
146
- l2_lambda: hash[:l2_lambda])
148
+ l2_lambda: hash[:l2_lambda],
149
+ use_bias: hash[:use_bias])
147
150
  simple_rnn
148
151
  end
149
152
 
@@ -154,14 +157,16 @@ module DNN
154
157
  weight_initializer: RandomNormal.new,
155
158
  bias_initializer: Zeros.new,
156
159
  l1_lambda: 0,
157
- l2_lambda: 0)
160
+ l2_lambda: 0,
161
+ use_bias: true)
158
162
  super(num_nodes,
159
163
  stateful: stateful,
160
164
  return_sequences: return_sequences,
161
165
  weight_initializer: weight_initializer,
162
166
  bias_initializer: bias_initializer,
163
167
  l1_lambda: l1_lambda,
164
- l2_lambda: l2_lambda)
168
+ l2_lambda: l2_lambda,
169
+ use_bias: use_bias)
165
170
  @activation = activation
166
171
  end
167
172
 
@@ -176,10 +181,10 @@ module DNN
176
181
  num_prev_nodes = @input_shape[1]
177
182
  @weight.data = Xumo::SFloat.new(num_prev_nodes, @num_nodes)
178
183
  @weight2.data = Xumo::SFloat.new(@num_nodes, @num_nodes)
179
- @bias.data = Xumo::SFloat.new(@num_nodes)
184
+ @bias.data = Xumo::SFloat.new(@num_nodes) if @bias
180
185
  @weight_initializer.init_param(self, @weight)
181
186
  @weight_initializer.init_param(self, @weight2)
182
- @bias_initializer.init_param(self, @bias)
187
+ @bias_initializer.init_param(self, @bias) if @bias
183
188
  @time_length.times do |t|
184
189
  @layers << SimpleRNN_Dense.new(@weight, @weight2, @bias, @activation)
185
190
  end
@@ -204,7 +209,8 @@ module DNN
204
209
  @h = h
205
210
  @c = c
206
211
  num_nodes = h.shape[1]
207
- a = x.dot(@weight.data) + h.dot(@weight2.data) + @bias.data
212
+ a = x.dot(@weight.data) + h.dot(@weight2.data)
213
+ a += @bias.data if @bias
208
214
 
209
215
  @forget = @forget_sigmoid.forward(a[true, 0...num_nodes])
210
216
  @g = @g_tanh.forward(a[true, num_nodes...(num_nodes * 2)])
@@ -230,7 +236,7 @@ module DNN
230
236
 
231
237
  @weight.grad += @x.transpose.dot(da)
232
238
  @weight2.grad += @h.transpose.dot(da)
233
- @bias.grad += da.sum(0)
239
+ @bias.grad += da.sum(0) if @bias
234
240
  dx = da.dot(@weight.data.transpose)
235
241
  dh = da.dot(@weight2.data.transpose)
236
242
  dc = dc2_tmp * @forget
@@ -247,7 +253,8 @@ module DNN
247
253
  weight_initializer: Utils.load_hash(hash[:weight_initializer]),
248
254
  bias_initializer: Utils.load_hash(hash[:bias_initializer]),
249
255
  l1_lambda: hash[:l1_lambda],
250
- l2_lambda: hash[:l2_lambda])
256
+ l2_lambda: hash[:l2_lambda],
257
+ use_bias: hash[:use_bias])
251
258
  lstm
252
259
  end
253
260
 
@@ -257,7 +264,8 @@ module DNN
257
264
  weight_initializer: RandomNormal.new,
258
265
  bias_initializer: Zeros.new,
259
266
  l1_lambda: 0,
260
- l2_lambda: 0)
267
+ l2_lambda: 0,
268
+ use_bias: true)
261
269
  super
262
270
  @cell = @params[:c] = Param.new
263
271
  end
@@ -286,7 +294,7 @@ module DNN
286
294
  def backward(dh2s)
287
295
  @weight.grad = Xumo::SFloat.zeros(*@weight.data.shape)
288
296
  @weight2.grad = Xumo::SFloat.zeros(*@weight2.data.shape)
289
- @bias.grad = Xumo::SFloat.zeros(*@bias.data.shape)
297
+ @bias.grad = Xumo::SFloat.zeros(*@bias.data.shape) if @bias
290
298
  unless @return_sequences
291
299
  dh = dh2s
292
300
  dh2s = Xumo::SFloat.zeros(dh.shape[0], @time_length, dh.shape[1])
@@ -315,10 +323,10 @@ module DNN
315
323
  num_prev_nodes = @input_shape[1]
316
324
  @weight.data = Xumo::SFloat.new(num_prev_nodes, @num_nodes * 4)
317
325
  @weight2.data = Xumo::SFloat.new(@num_nodes, @num_nodes * 4)
318
- @bias.data = Xumo::SFloat.new(@num_nodes * 4)
326
+ @bias.data = Xumo::SFloat.new(@num_nodes * 4) if @bias
319
327
  @weight_initializer.init_param(self, @weight)
320
328
  @weight_initializer.init_param(self, @weight2)
321
- @bias_initializer.init_param(self, @bias)
329
+ @bias_initializer.init_param(self, @bias) if @bias
322
330
  @time_length.times do |t|
323
331
  @layers << LSTM_Dense.new(@weight, @weight2, @bias)
324
332
  end
@@ -342,15 +350,19 @@ module DNN
342
350
  num_nodes = h.shape[1]
343
351
  @weight_a = @weight.data[true, 0...(num_nodes * 2)]
344
352
  @weight2_a = @weight2.data[true, 0...(num_nodes * 2)]
345
- bias_a = @bias.data[0...(num_nodes * 2)]
346
- a = x.dot(@weight_a) + h.dot(@weight2_a) + bias_a
353
+ a = x.dot(@weight_a) + h.dot(@weight2_a)
354
+ a += @bias.data[0...(num_nodes * 2)] if @bias
347
355
  @update = @update_sigmoid.forward(a[true, 0...num_nodes])
348
356
  @reset = @reset_sigmoid.forward(a[true, num_nodes..-1])
349
357
 
350
358
  @weight_h = @weight.data[true, (num_nodes * 2)..-1]
351
359
  @weight2_h = @weight2.data[true, (num_nodes * 2)..-1]
352
- bias_h = @bias.data[(num_nodes * 2)..-1]
353
- @tanh_h = @tanh.forward(x.dot(@weight_h) + (h * @reset).dot(@weight2_h) + bias_h)
360
+ @tanh_h = if @bias
361
+ bias_h = @bias.data[(num_nodes * 2)..-1]
362
+ @tanh.forward(x.dot(@weight_h) + (h * @reset).dot(@weight2_h) + bias_h)
363
+ else
364
+ @tanh.forward(x.dot(@weight_h) + (h * @reset).dot(@weight2_h))
365
+ end
354
366
  h2 = (1 - @update) * h + @update * @tanh_h
355
367
  h2
356
368
  end
@@ -363,7 +375,7 @@ module DNN
363
375
  dx = dtanh_h.dot(@weight_h.transpose)
364
376
  dweight2_h = (@h * @reset).transpose.dot(dtanh_h)
365
377
  dh += dtanh_h.dot(@weight2_h.transpose) * @reset
366
- dbias_h = dtanh_h.sum(0)
378
+ dbias_h = dtanh_h.sum(0) if @bias
367
379
 
368
380
  dreset = @reset_sigmoid.backward(dtanh_h.dot(@weight2_h.transpose) * @h)
369
381
  dupdate = @update_sigmoid.backward(dh2 * @tanh_h - dh2 * @h)
@@ -372,11 +384,11 @@ module DNN
372
384
  dx += da.dot(@weight_a.transpose)
373
385
  dweight2_a = @h.transpose.dot(da)
374
386
  dh += da.dot(@weight2_a.transpose)
375
- dbias_a = da.sum(0)
387
+ dbias_a = da.sum(0) if @bias
376
388
 
377
389
  @weight.grad += Xumo::SFloat.hstack([dweight_a, dweight_h])
378
390
  @weight2.grad += Xumo::SFloat.hstack([dweight2_a, dweight2_h])
379
- @bias.grad += Xumo::SFloat.hstack([dbias_a, dbias_h])
391
+ @bias.grad += Xumo::SFloat.hstack([dbias_a, dbias_h]) if @bias
380
392
  [dx, dh]
381
393
  end
382
394
  end
@@ -390,7 +402,8 @@ module DNN
390
402
  weight_initializer: Utils.load_hash(hash[:weight_initializer]),
391
403
  bias_initializer: Utils.load_hash(hash[:bias_initializer]),
392
404
  l1_lambda: hash[:l1_lambda],
393
- l2_lambda: hash[:l2_lambda])
405
+ l2_lambda: hash[:l2_lambda],
406
+ use_bias: hash[:use_bias])
394
407
  gru
395
408
  end
396
409
 
@@ -400,7 +413,8 @@ module DNN
400
413
  weight_initializer: RandomNormal.new,
401
414
  bias_initializer: Zeros.new,
402
415
  l1_lambda: 0,
403
- l2_lambda: 0)
416
+ l2_lambda: 0,
417
+ use_bias: true)
404
418
  super
405
419
  end
406
420
 
@@ -411,10 +425,10 @@ module DNN
411
425
  num_prev_nodes = @input_shape[1]
412
426
  @weight.data = Xumo::SFloat.new(num_prev_nodes, @num_nodes * 3)
413
427
  @weight2.data = Xumo::SFloat.new(@num_nodes, @num_nodes * 3)
414
- @bias.data = Xumo::SFloat.new(@num_nodes * 3)
428
+ @bias.data = Xumo::SFloat.new(@num_nodes * 3) if @bias
415
429
  @weight_initializer.init_param(self, @weight)
416
430
  @weight_initializer.init_param(self, @weight2)
417
- @bias_initializer.init_param(self, @bias)
431
+ @bias_initializer.init_param(self, @bias) if @bias
418
432
  @time_length.times do |t|
419
433
  @layers << GRU_Dense.new(@weight, @weight2, @bias)
420
434
  end
@@ -20,14 +20,14 @@ module DNN
20
20
  dnn_class.new
21
21
  end
22
22
 
23
- # TODO
24
- # Don't want to write an implementation of the activation function in utils, so we will consider it later.
23
+ # Return the result of the sigmoid function.
25
24
  def self.sigmoid(x)
26
- 1 / (1 + NMath.exp(-x))
25
+ Sigmoid.new.forward(x)
27
26
  end
28
27
 
28
+ # Return the result of the softmax function.
29
29
  def self.softmax(x)
30
- NMath.exp(x) / NMath.exp(x).sum(1).reshape(x.shape[0], 1)
30
+ SoftmaxCrossEntropy.softmax(x)
31
31
  end
32
32
  end
33
33
  end
@@ -0,0 +1,60 @@
1
+ require "csv"
2
+ require_relative "downloader"
3
+
4
+ module DNN
5
+ class DNN_Iris_LoadError < DNN_Error; end
6
+
7
+ module Iris
8
+ URL_CSV = "https://archive.ics.uci.edu/ml/machine-learning-databases/iris/iris.data"
9
+
10
+ # Iris-setosa
11
+ SETOSA = 0
12
+ # Iris-versicolor
13
+ VERSICOLOR = 1
14
+ # Iris-virginica
15
+ VIRGINICA = 2
16
+
17
+ def self.downloads
18
+ return if File.exist?(url_to_file_name(URL_CSV))
19
+ Downloader.download(URL_CSV)
20
+ end
21
+
22
+ def self.load(shuffle = false, shuffle_seed = rand(1 << 31))
23
+ downloads
24
+ csv_array = CSV.read(url_to_file_name(URL_CSV)).select { |a| a.length > 0 }
25
+ x = Numo::SFloat.zeros(csv_array.length, 4)
26
+ y = Numo::SFloat.zeros(csv_array.length)
27
+ csv_array.each.with_index do |(sepal_length, sepal_width, petal_length, petal_width, classes), i|
28
+ x[i, 0] = sepal_length.to_f
29
+ x[i, 1] = sepal_width.to_f
30
+ x[i, 2] = petal_length.to_f
31
+ x[i, 3] = petal_width.to_f
32
+ y[i] = case classes
33
+ when "Iris-setosa"
34
+ SETOSA
35
+ when "Iris-versicolor"
36
+ VERSICOLOR
37
+ when "Iris-virginica"
38
+ VIRGINICA
39
+ else
40
+ raise DNN_Iris_LoadError.new("Unknown class name '#{classes}' for iris")
41
+ end
42
+ end
43
+ if shuffle
44
+ orig_seed = Random::DEFAULT.seed
45
+ srand(shuffle_seed)
46
+ indexs = (0...csv_array.length).to_a.shuffle
47
+ x[indexs, true] = x
48
+ y[indexs] = y
49
+ srand(orig_seed)
50
+ end
51
+ [x, y]
52
+ end
53
+
54
+ private_class_method
55
+
56
+ def self.url_to_file_name(url)
57
+ __dir__ + "/" + url.match(%r`.+/(.+)$`)[1]
58
+ end
59
+ end
60
+ end
data/lib/dnn/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module DNN
2
- VERSION = "0.9.3"
2
+ VERSION = "0.9.4"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruby-dnn
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.3
4
+ version: 0.9.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - unagiootoro
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2019-05-08 00:00:00.000000000 Z
11
+ date: 2019-06-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: numo-narray
@@ -101,6 +101,7 @@ files:
101
101
  - bin/console
102
102
  - bin/setup
103
103
  - examples/cifar10_example.rb
104
+ - examples/iris_example.rb
104
105
  - examples/mnist_conv2d_example.rb
105
106
  - examples/mnist_example.rb
106
107
  - examples/mnist_lstm_example.rb
@@ -126,6 +127,7 @@ files:
126
127
  - lib/dnn/lib/cifar10.rb
127
128
  - lib/dnn/lib/downloader.rb
128
129
  - lib/dnn/lib/image.rb
130
+ - lib/dnn/lib/iris.rb
129
131
  - lib/dnn/lib/mnist.rb
130
132
  - lib/dnn/version.rb
131
133
  - ruby-dnn.gemspec