Skip to content

Instantly share code, notes, and snippets.

@iamvery
Last active June 24, 2021 17:57
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save iamvery/985abe4daa955a63b1e0ba460b3b9926 to your computer and use it in GitHub Desktop.
Save iamvery/985abe4daa955a63b1e0ba460b3b9926 to your computer and use it in GitHub Desktop.

machine learning example

run the example:

$ ruby houses.rb

The example uses gnuplot which may need to be installed on your system. See the gem for more details

3,10
15,4
30,35
10,12
7,5
35,30
25,25
20,23
30,27
17,13
15,20
source "https://rubygems.org"
gem "gnuplot"
gem "highline"
class House
attr_reader :size, :value
def initialize(size:, value:)
@size = size.to_f
@value = value.to_f
end
end
require "./house"
class HouseData
def initialize
@csv = CSV.open("data.txt", "a+b")
end
def houses
@csv.rewind
@csv.map { |row|
House.new(size: row.first, value: row.last)
}
end
def add(row)
@csv << row
end
end
require "highline"
require "csv"
require "gnuplot"
require "./house_data"
require "./plot"
require "./learn"
data = HouseData.new
cli = HighLine.new
cli.choose do |menu|
menu.prompt = "What would you like to do?"
menu.choice("Add Data") {
size = cli.ask("Size (in 100s sq. ft.):")
value = cli.ask("Value (in $10,000s):")
data.add([size, value])
}
menu.choice("View Data") {
sizes = data.houses.map(&:size)
values = data.houses.map(&:value)
Plot.new(sizes, values).draw
}
menu.choice("Predict Value") {
raw_data = data.houses.map { |h| [h.size, h.value] }
learn = Learn.new(raw_data)
size = cli.ask("Size (in 100s sq. ft.):")
predicted_value = (learn.predict(size.to_f) * 10).round
cli.say("Such a house is worth about $#{predicted_value}k")
}
end
class Learn
LINE_THROUGH_ORIGIN = ->(slope, x) { slope * x }.curry
MEAN_ERRORS = ->(data, hypothesis, slope) {
errors = data.map { |(x, y)| hypothesis.(slope, x) - y }
errors.reduce(:+) / errors.count.to_f
}.curry
MINIMIZE = ->(calculate_cost, learning_rate: 0.2, iterations: 10_000, &callback) {
(1..iterations).reduce(0.0) do |slope, iteration|
cost = calculate_cost.(slope)
direction = cost < 0 ? 1 : -1
step = direction * learning_rate / (iteration.to_f)
(slope += step).tap { callback.(slope) if callback }
end
}
def initialize(data)
slope = MINIMIZE.(MEAN_ERRORS.(data, LINE_THROUGH_ORIGIN))
@predictor = LINE_THROUGH_ORIGIN.(slope)
end
def predict(input)
@predictor.(input)
end
end
require "gnuplot"
class Plot
attr_reader :sizes, :values
def initialize(sizes, values)
@sizes = sizes
@values = values
end
def draw
Gnuplot.open do |gp|
Gnuplot::Plot.new(gp) do |plot|
plot.xlabel "Size in 100s sq. ft."
plot.ylabel "Value in $10,000s"
plot.xrange "[0:50]"
plot.yrange "[0:50]"
plot.data << Gnuplot::DataSet.new([sizes,values])
end
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment