lib/dnn/core/models.rb in ruby-dnn-1.1.1 vs lib/dnn/core/models.rb in ruby-dnn-1.1.2

- old
+ new

@@ -107,10 +107,11 @@ end # This class deals with the model of the network. class Model < Chain attr_accessor :optimizer + attr_accessor :loss_weights attr_reader :last_log # Load marshal model. # @param [String] file_name File name of marshal model to load. # @return [DNN::Models::Model] Return the loaded model. @@ -124,10 +125,11 @@ def initialize super @optimizer = nil @loss_func = nil @built = false + @loss_weights = nil @callbacks = [] @last_log = {} end def call(input_tensors) @@ -137,19 +139,21 @@ end # Set optimizer and loss_func to model. # @param [DNN::Optimizers::Optimizer] optimizer Optimizer to use for learning. # @param [DNN::Losses::Loss] loss_func Loss function to use for learning. - def setup(optimizer, loss_func) + # @param [Array | NilClass] loss_weights Setting loss weights contribution. + def setup(optimizer, loss_func, loss_weights: nil) unless optimizer.is_a?(Optimizers::Optimizer) raise TypeError, "optimizer:#{optimizer.class} is not an instance of DNN::Optimizers::Optimizer class." end unless loss_func.is_a?(Losses::Loss) || loss_func.is_a?(Array) raise TypeError, "loss_func:#{loss_func.class} is not an instance of DNN::Losses::Loss or Array class." end @optimizer = optimizer self.loss_func = loss_func + @loss_weights = loss_weights end def loss_func @loss_func end @@ -176,22 +180,25 @@ # @param [Integer] batch_size Batch size used for one training. # @param [Integer] initial_epoch Initial epoch. # @param [Array | NilClass] test If you to test the model for every 1 epoch, # specify [x_test, y_test]. Don't test to the model, specify nil. # @param [Boolean] verbose Set true to display the log. If false is set, the log is not displayed. + # @param [Boolean] accuracy Set true to compute the accuracy. def train(x, y, epochs, batch_size: 1, initial_epoch: 1, test: nil, - verbose: true) + verbose: true, + accuracy: true) check_xy_type(x, y) train_iterator = Iterator.new(x, y) train_by_iterator(train_iterator, epochs, batch_size: batch_size, initial_epoch: initial_epoch, test: test, - verbose: verbose) + verbose: verbose, + accuracy: accuracy) end alias fit train # Start training by iterator. @@ -201,15 +208,17 @@ # @param [Integer] batch_size Batch size used for one training. # @param [Integer] initial_epoch Initial epoch. # @param [Array | NilClass] test If you to test the model for every 1 epoch, # specify [x_test, y_test]. Don't test to the model, specify nil. # @param [Boolean] verbose Set true to display the log. If false is set, the log is not displayed. + # @param [Boolean] accuracy Set true to compute the accuracy. def train_by_iterator(train_iterator, epochs, batch_size: 1, initial_epoch: 1, test: nil, - verbose: true) + verbose: true, + accuracy: true) raise DNNError, "The model is not optimizer setup complete." unless @optimizer raise DNNError, "The model is not loss_func setup complete." unless @loss_func num_train_datas = train_iterator.num_datas num_train_datas = num_train_datas / batch_size * batch_size if train_iterator.last_round_down @@ -240,15 +249,22 @@ print log if verbose end if test acc, loss = if test.is_a?(Array) - evaluate(test[0], test[1], batch_size: batch_size) + evaluate(test[0], test[1], batch_size: batch_size, accuracy: accuracy) else - evaluate_by_iterator(test, batch_size: batch_size) + evaluate_by_iterator(test, batch_size: batch_size, accuracy: accuracy) end - print " " + metrics_to_str({ accuracy: acc, test_loss: loss }) if verbose + if verbose + metrics = if accuracy + { accuracy: acc, test_loss: loss } + else + { test_loss: loss } + end + print " " + metrics_to_str(metrics) + end end puts "" if verbose call_callbacks(:after_epoch) end nil @@ -283,21 +299,20 @@ DNN.learning_phase = true output_tensors = call(Tensor.convert(x)) if output_tensors.is_a?(Array) loss_data = [] output_tensors.each.with_index do |out, i| - loss = if i == 0 - @loss_func[i].loss(out, Tensor.convert(y[i]), layers) - else - @loss_func[i].loss(out, Tensor.convert(y[i])) - end + loss_opt = {} + loss_opt[:layers] = layers if i == 0 + loss_opt[:loss_weight] = @loss_weights[i] if @loss_weights + loss = @loss_func[i].loss(out, Tensor.convert(y[i]), **loss_opt) loss_data << loss.data.to_f loss.link.backward(Xumo::SFloat.ones(y[i][0...1, false].shape[0], 1)) end else out = output_tensors - loss = @loss_func.loss(out, Tensor.convert(y), layers) + loss = @loss_func.loss(out, Tensor.convert(y), layers: layers) loss_data = loss.data.to_f loss.link.backward(Xumo::SFloat.ones(y[0...1, false].shape[0], 1)) end @optimizer.update(get_all_trainable_params) @last_log[:train_loss] = loss_data @@ -308,20 +323,22 @@ # Evaluate model and get accuracy and loss of test data. # @param [Numo::SFloat] x Input test data. # @param [Numo::SFloat] y Output test data. # @param [Integer] batch_size Batch size used for one test. # @return [Array] Returns the test data accuracy and mean loss in the form [accuracy, mean_loss]. - def evaluate(x, y, batch_size: 100) + # If accuracy is not needed returns in the form [nil, mean_loss]. + def evaluate(x, y, batch_size: 100, accuracy: true) check_xy_type(x, y) - evaluate_by_iterator(Iterator.new(x, y, random: false), batch_size: batch_size) + evaluate_by_iterator(Iterator.new(x, y, random: false), batch_size: batch_size, accuracy: accuracy) end # Evaluate model by iterator. # @param [DNN::Iterator] test_iterator Iterator used for testing. # @param [Integer] batch_size Batch size used for one test. # @return [Array] Returns the test data accuracy and mean loss in the form [accuracy, mean_loss]. - def evaluate_by_iterator(test_iterator, batch_size: 100) + # If accuracy is not needed returns in the form [nil, mean_loss]. + def evaluate_by_iterator(test_iterator, batch_size: 100, accuracy: true) num_test_datas = test_iterator.num_datas batch_size = batch_size >= num_test_datas ? num_test_datas : batch_size if @loss_func.is_a?(Array) total_correct = Array.new(@loss_func.length, 0) sum_loss = Array.new(@loss_func.length, 0) @@ -329,55 +346,58 @@ total_correct = 0 sum_loss = 0 end max_steps = (num_test_datas.to_f / batch_size).ceil test_iterator.foreach(batch_size) do |x_batch, y_batch| - correct, loss_value = test_on_batch(x_batch, y_batch) + correct, loss_value = test_on_batch(x_batch, y_batch, accuracy: accuracy) if @loss_func.is_a?(Array) @loss_func.each_index do |i| - total_correct[i] += correct[i] + total_correct[i] += correct[i] if accuracy sum_loss[i] += loss_value[i] end else - total_correct += correct + total_correct += correct if accuracy sum_loss += loss_value end end + acc = nil if @loss_func.is_a?(Array) mean_loss = Array.new(@loss_func.length, 0) - acc = Array.new(@loss_func.length, 0) + acc = Array.new(@loss_func.length, 0) if accuracy @loss_func.each_index do |i| mean_loss[i] += sum_loss[i] / max_steps - acc[i] += total_correct[i].to_f / num_test_datas + acc[i] += total_correct[i].to_f / num_test_datas if accuracy end else mean_loss = sum_loss / max_steps - acc = total_correct.to_f / num_test_datas + acc = total_correct.to_f / num_test_datas if accuracy end @last_log[:test_loss] = mean_loss @last_log[:test_accuracy] = acc [acc, mean_loss] end # Evaluate once. # @param [Numo::SFloat | Array] x Input test data. # @param [Numo::SFloat | Array] y Output test data. - # @return [Array] Returns the test data accuracy and mean loss in the form [accuracy, mean_loss]. - def test_on_batch(x, y) + # @return [Array] Returns the test data accuracy and mean loss in the form [accuracy, loss]. + # If accuracy is not needed returns in the form [nil, loss]. + def test_on_batch(x, y, accuracy: true) call_callbacks(:before_test_on_batch) DNN.learning_phase = false output_tensors = call(Tensor.convert(x)) + correct = nil if output_tensors.is_a?(Array) - correct = [] + correct = [] if accuracy loss_data = [] output_tensors.each.with_index do |out, i| - correct << accuracy(out.data, y[i]) + correct << accuracy(out.data, y[i]) if accuracy loss = @loss_func[i].(out, Tensor.convert(y[i])) loss_data << loss.data.to_f end else out = output_tensors - correct = accuracy(out.data, y) + correct = accuracy(out.data, y) if accuracy loss = @loss_func.(out, Tensor.convert(y)) loss_data = loss.data.to_f end call_callbacks(:after_test_on_batch) [correct, loss_data]