# Model based on https://www.kaggle.com/autuanliuyc/logistic-regression-with-tensorflow

require "bundler/setup"
require "tensor_stream"

tf = TensorStream

rows = File.readlines(File.join("samples", "datasets", "iris.data")).map {|l| l.chomp.split(",") }

iris = rows[0...100].shuffle!

transformed_data = iris.collect { |row|
  row[0, 4].map(&:to_f)
}

columns = (0..3).map { |i|
  transformed_data.map { |row| row[i] }
}

# Normalize data values before feeding into network
normalize = ->(val, high, low) { (val - low) / (high - low) } # maps input to float between 0 and 1

transformed_data.map! do |row|
  row.map.with_index do |val, j|
    max, min = columns[j].max, columns[j].min
    normalize.call(val, max, min)
  end
end

srand(5)
seed = 5
tf.set_random_seed(seed)

train_x = transformed_data[0..50].map { |x| x[0..3].map(&:to_f) }
train_y = iris[0..50].map { |x| x[4] == "Iris-setosa" ? 0.0 : 1.0 }

test_x = transformed_data[51..100].map { |x| x[0..3].map(&:to_f) }
test_y = iris[51..100].map { |x| x[4] == "Iris-setosa" ? 0.0 : 1.0 }

A = tf.random_normal([4, 1]).var
b = tf.random_normal([1, 1]).var

init = tf.global_variables_initializer
sess = tf.session
sess.run(init)

data = Float.placeholder shape: [nil, 4]
target = Float.placeholder shape: [nil, 1]

mod = data.matmul(A) + b

loss = tf.nn.sigmoid_cross_entropy_with_logits(logits: mod, labels: target).reduce :mean

learning_rate = 0.003
batch_size = 30
iter_num = 1500

optimizer = TensorStream::Train::GradientDescentOptimizer.new(learning_rate)
goal = optimizer.minimize(loss)
prediction = tf.sigmoid(mod).round

# Bool into float32 type
correct = (prediction == target).cast

# Average
accuracy = correct.reduce :mean

loss_trace = []
train_acc = []
test_acc = []

(0..iter_num).each do |epoch|
  batch_train_X = train_x
  batch_train_y = [train_y].transpose
  sess.run(goal, feed_dict: {data => batch_train_X, target => batch_train_y})

  if epoch % 50 == 0
    temp_loss = sess.run(loss, feed_dict: {data => batch_train_X, target => batch_train_y})
    temp_train_acc = sess.run(accuracy, feed_dict: {data => batch_train_X, target => batch_train_y})
    temp_test_acc = sess.run(accuracy, feed_dict: {data => test_x, target => [test_y].transpose})
    puts "epoch #{epoch}, loss #{temp_loss} train acc: #{temp_train_acc}, test acc: #{temp_test_acc}"
  end
end
