• Stars
    star
    506
  • Rank 83,949 (Top 2 %)
  • Language
    Ruby
  • License
    MIT License
  • Created almost 6 years ago
  • Updated over 3 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

A ground-up and standalone reimplementation of TensorFlow for ruby. Comes with a pure ruby and OpenCL opcode evaluator

Gem VersionGem VersionCircleCI Join the chat at https://gitter.im/tensor_stream/Lobby

TensorStream

TensorStream is an opensource framework for machine learning for ruby, its goal is to allow machine learning models to be easily built and run them in various hardware like GPUs and CPUs. It is heavily based on TensorFlow with the goal of being able to easily port its higher level libraries and model examples. As such it is also based on data flow graphs wherein you define computations and data flows between those computations in order to achieve the desired output.

TensorStream is designed to support various backends with a Pure Ruby and OpenCL implementation. These implementations are designed to work together, you can perform training on an OpenCL implementation (where you have a GPU) and then run the resulting trained model on a Pure Ruby implementation where you can deploy anywhere that you can run ruby on. TensorStream has been tested to run on most ruby implementations like MRI, JRuby and TruffleRuby.

Goals & Features

  • Easy to use - Improve model readability
  • Replicates most of the commonly used low-level tensorflow ops (tf.add, tf.constant, tf.placeholder, tf.matmul, tf.sin etc...)
  • Supports auto-differentiation using formal derivation
  • Extensible - use your own opcode evaluator (OpenCL and Pure ruby currently supported)
  • wide support - Run on most ruby implementations as well as hardware acceleration on OpenCL supported hardware

Compatibility

TensorStream comes with a pure ruby and OpenCL implementation out of the box. The pure ruby implementation is known to work with most ruby implementations including TruffleRuby, JRuby as well as jit enabled versions of mri (ruby-2.6.0).

OpenCL is supported only on mri implementations of ruby. This can be enabled by adding OpenCL evaluator gem (Make sure you have OpenCL drivers installed correctly on your system):

gem 'tensor_stream-opencl'

and then (without bundler)

require 'tensor_stream/opencl'

OpenCL is basically a requirement for deep learning and image processing tasks as the ruby implementation is too slow even with jit speedups using latest ruby implementations.

OpenCL kernels used by tensorstream can be found at tensor_stream/lib/evaluator/opencl/kernels. These are non specific and should work with any device that supports OpenCL including intel GPUs and CPUs, as well as GPUs from Nvidia and AMD.

Installation

Installation is easy, no need to mess with docker, python, clang or other shennanigans, works with both mri and jruby out of the box.

Add this line to your application's Gemfile:

gem 'tensor_stream'

And then execute:

$ bundle

Or install it yourself as:

$ gem install tensor_stream

Usage

Usage is similar to how you would use TensorFlow except with ruby syntax. There are also enhancements to the syntax to make it as consice as possible.

Linear regression sample:

require 'tensor_stream'

tf = TensorStream

learning_rate = 0.01
training_epochs = 1000
display_step = 50

train_x = [3.3, 4.4, 5.5, 6.71, 6.93, 4.168, 9.779, 6.182, 7.59, 2.167,
           7.042, 10.791, 5.313, 7.997, 5.654, 9.27, 3.1]

train_y = [1.7, 2.76, 2.09, 3.19, 1.694, 1.573, 3.366, 2.596, 2.53, 1.221,
           2.827, 3.465, 1.65, 2.904, 2.42, 2.94, 1.3]

n_samples = train_x.size

x_value = Float.placeholder
y_value = Float.placeholder

# Set model weights
weight = rand.t.var name: "weight"

bias = rand.t.var name: "bias"

# Construct a linear model
pred = x_value * weight + bias

# Mean squared error
cost = ((pred - y_value)**2).reduce / (2 * n_samples)

# Other optimizers --
#
# optimizer = TensorStream::Train::MomentumOptimizer.new(learning_rate, momentum, use_nesterov: true).minimize(cost)
# optimizer = TensorStream::Train::AdamOptimizer.new(learning_rate).minimize(cost)
# optimizer = TensorStream::Train::AdadeltaOptimizer.new(1.0).minimize(cost)
# optimizer = TensorStream::Train::AdagradOptimizer.new(0.01).minimize(cost)
# optimizer = TensorStream::Train::RMSPropOptimizer.new(0.01, centered: true).minimize(cost)
optimizer = TensorStream::Train::GradientDescentOptimizer.new(learning_rate).minimize(cost)

# Initialize the variables (i.e. assign their default value)
init = tf.global_variables_initializer

tf.session do |sess|
  start_time = Time.now
  sess.run(init)

  (0..training_epochs).each do |epoch|
    train_x.zip(train_y).each do |x, y|
      sess.run(optimizer, feed_dict: { x_value => x, y_value => y })
    end

    if (epoch + 1) % display_step == 0
      c = sess.run(cost, feed_dict: { x_value => train_x, y_value => train_y })
      puts("Epoch:", '%04d' % (epoch + 1), "cost=", c, \
           "W=", sess.run(weight), "b=", sess.run(bias))
    end
  end

  puts "Optimization Finished!"
  training_cost = sess.run(cost, feed_dict: { x_value => train_x, y_value => train_y })
  puts "Training cost=", training_cost, "W=", sess.run(weight), "b=", sess.run(bias), '\n'
  puts "time elapsed ", Time.now.to_i - start_time.to_i
end

You can take a look at spec/tensor_stream/operation_spec.rb for a list of supported ops and various examples and test cases used. Of course these contain only a sliver of what TensorFlow can do, so feel free to file a PR to add requested ops and test cases.

Other working samples can also be seen under tensor_stream/samples.

Samples that are used for development and are still being made to work can be found under test_samples

Python to Ruby guide

Not all ops are available. Available ops are defined in lib/tensor_stream/ops.rb, corresponding gradients are found at lib/tensor_stream/math_gradients.

There are also certain differences with regards to naming conventions, and named parameters:

Variables and Constants

To make referencing python examples easier it is recommended to use "tf" as the TensorStream namespace

At the beginning

tf = TensorStream # recommended to use tf since most sample models on the net use this
ts = TensorStream # use this if you plan to use TensorStream only features, so other devs will know about that

Note the difference in named and optional parameters

Python

w = ts.Variable(0, name='weights')
w = ts.Variable(0, 'weights')

Ruby

w = ts.variable(0, name: 'weights')
c = ts.constant(1.0)

# concise way when initializing using a constant
w = 0.t.var name: 'weights'
c = 1.0.t

Calling .t to Integer, Array and Float types converts it into a tensor

Shapes

Python

x = tf.placeholder(tf.float32, shape=(1024, 1024))
x = tf.placeholder(tf.float32, shape=(None, 1024))

ruby supports symbols for specifying data types, nil can be used for None

Ruby

x = ts.placeholder(:float32, shape: [1024, 1024])
x = ts.placeholder(:float32, shape: [nil, 1024])

# Another a bit more terse way
x = Float.placeholder shape: [1024, 1024]
y = Float.placeholder shape: [nil, 1024]

For debugging, each operation or tensor supports the to_math method

X = ts.placeholder("float")
Y = ts.placeholder("float")
W = ts.variable(rand, name: "weight")
b = ts.variable(rand, name: "bias")
pred = X * W + b
cost = ts.reduce_sum(ts.pow(pred - Y, 2)) / ( 2 * 10)
cost.to_math # "(reduce_sum(|((((Placeholder: * weight) + bias) - Placeholder_2:)^2)|) / 20.0)"

breakpoints can also be set, block will be evaluated during computation

a = ts.constant([2,2])
b = ts.constant([3,3])

f = ts.matmul(a, b).breakpoint! { |tensor, a, b, result_value| binding.pry }

ts.session.run(f)

OpenCL

For OpenCL support, make sure that the required OpenCL drivers for your hardware are correctly installed on your system. Also OpenCL only supports ruby-mri at the moment.

To use, include the following gem in your project:

gem 'tensor_stream-opencl'

To use the opencl evaluator instead of the ruby evaluator simply require it (if using rails this should be loaded automatically).

require 'tensor_stream/opencl'

Adding the OpenCL evaluator should expose additional devices available to tensor_stream

ts.list_local_devices
# ["job:localhost/ts:ruby:cpu", "job:localhost/ts:opencl:apple:0", "job:localhost/ts:opencl:apple:1"]

Here we see 1 "ruby" cpu device and 2 opencl "apple" devices (Intel CPU, Intel Iris GPU)

By default TensorStream will determine using the given evaluators the best possible placement for each tensor operation

require 'tensor_stream/opencl'

# set session to use the opencl evaluator
sess = ts.session

sess.run(....) # do stuff

You can manually place operations using ts.device e.g:

ts = TensorStream
# Creates a graph. place in the first OpenCL CPU device

a, b = ts.device('/cpu:0') do
  a = ts.constant([1.0, 2.0, 3.0, 4.0, 5.0, 6.0], shape: [2, 3], name: 'a')
  b = ts.constant([1.0, 2.0, 3.0, 4.0, 5.0, 6.0], shape: [3, 2], name: 'b')
  [a, b]
end

c = ts.device('/device:GPU:0') do
  ts.matmul(a, b)
end

# Creates a session with log_device_placement set to True.
sess = ts.session(log_device_placement: true)
# Runs the op.
print(sess.run(c))

# a : apple:0
# b : apple:0
# a_1 : apple:0
# b_1 : apple:0
# matmul:0 : apple:1
# [[22.0, 28.0], [49.0, 64.0]] => nil

To force the ruby evaluator even with the OpenCL evaluator loaded you can use:

ts.device('/ts:ruby:cpu') do
    # put ops here
end

Note that the OpenCL evaluator provides speedup if you are using large tensors, tensors that are only using scalars like the linear regression sample will actually be slower.

samples/nearest_neighbor.rb contains a sample that uses opencl.

OpenCL support is maintained as a separate project at:

https://github.com/jedld/tensor_stream-opencl

Export Import Models from tensorflow

Experimental support for parsing and exporting pbtext files are supported:

Exporting

a = ts.constant([1.0, 1.0])
b = ts.constant([1.5, 1.5])
f = a + b

File.write('my_model.pbtext', f.graph.as_graph_def)

Importing (Experimental)

Note that not all tensorflow ops are supported, warnings will be showed if a certain operation is not supported yet.

  pbtext = File.read(File.join('linear_regression.pbtxt'))

  # create a graph from pbtext file
  graph = TensorStream::Graph.parse_from_string(pbtext)

  # reference a tensor by name from the created graph,
  # for example you have a tensor named out
  tensor = graph.get_tensor_by_name("out")

  # set graph as default and do operations on it
  graph.as_default do
    sess = ts.session
    expect(tr(sess.run(tensor))).to eq([[1.0, 1.0], [1.0, 1.0]])
  end

Visualization

tensorstream does not support tensorboard yet, but a graphml generator is included:

tf = TensorStream
a = tf.constant(1.0)
b = tf.constant(2.0)
result = a + b
sess = tf.session
sess.run(result)

File.write('gradients.graphml', TensorStream::Graphml.new.get_string(result)) # dump graph only
File.write('gradients.graphml', TensorStream::Graphml.new.get_string(result, sess)) # dump with values from session

the resulting graphml is designed to work with yED, after loading the graph change layout to "Flowchart" for best results

Exporting to TensorFlow

Still in alpha but tensorstream supports TensorFlows as_graph_def serialization method:

tf = TensorStream
a = tf.constant(1.0)
b = tf.constant(2.0)
result = a + b
File.write("model.pbtext", result.graph.as_graph_def)

Performance notes

Comparative performance with respect to other ruby libraries have not yet been performed. However it is notable that TruffleRuby and ruby-2.6.0 performs considerably better with respect to previous versions of ruby(< 2.6)

Benchmarks running samples/linear_regression.rb with tensor_stream 1.0.0 on an AMD(R) Ryzen(TM) 3 1300X CPU

ruby 2.5

$ ruby -v
ruby 2.5.1p57 (2018-03-29 revision 63029) [x86_64-linux]
$ ruby samples/linear_regression.rb
296 seconds 3000 epochs

ruby 2.6.0

$ ruby -v
ruby 2.6.0p0 (2018-12-25 revision 66547) [x86_64-linux]
$ ruby samples/linear_regression.rb
232 seconds 10000 epochs

ruby --jit samples/linear_regression.rb
222 seconds 10000 epochs

truffleruby

$ ruby -v
truffleruby 1.0.0-rc10, like ruby 2.4.4, GraalVM CE Native [x86_64-linux]
246 seconds 10000 epochs

jruby

$ ruby -v
jruby 9.2.0.0 (2.5.0) 2018-05-24 81156a8 OpenJDK 64-Bit Server VM 25.191-b12 on 1.8.0_191-8u191-b12-0ubuntu0.18.04.1-b12 +jit [linux-x86_64]
205 seconds 10000 epochs

For training large networks that works on images, the opencl evaluator is the only way to go.

Roadmap

  • Docs
  • Complete low-level op support
  • SciRuby evaluator
  • Opencl evaluator
  • TensorFlow savemodel compatibility

Issues

  • This is an early preview release and many things still don't work
  • Performance is not great, at least until the opencl and/or sciruby backends are complete
  • However if you really need an op supported please feel free to file a pull request with the corresponding failing test (see spec/operation_spec.rb)

Development

After checking out the repo, run bin/setup to install dependencies. Then, run rake spec to run the tests. You can also run bin/console for an interactive prompt that will allow you to experiment.

To install this gem onto your local machine, run bundle exec rake install. To release a new version, update the version number in version.rb, and then run bundle exec rake release, which will create a git tag for the version, push git commits and tags, and push the .gem file to rubygems.org.

Contributing

Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/tensor_stream. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the Contributor Covenant code of conduct.

License

The gem is available as open source under the terms of the MIT License.

More Repositories

1

pretentious

Tool for writing characterization tests - Generate RSpec or minitest specs based on existing behavior in a straightforward manner
Ruby
98
star
2

resugan

simple, powerful and unobstrusive event driven architecture framework for ruby
Ruby
81
star
3

natural_20

This is a text-based game engine that implements the D&D 5th edition ruleset. A sample adventure is included in this repository
Ruby
69
star
4

brains-jruby

A feedforward neural network implementation for JRuby
Ruby
60
star
5

tensor_stream-opencl

An OpenCL backend for TensorStream
Ruby
27
star
6

multi_string_replace

A fast multiple string replace library for ruby. Uses a C implementation of the Ahoโ€“Corasick Algorithm based on https://github.com/morenice/ahocorasick while adding support for on the fly multiple string replacement. Faster alternative to String.gsub when dealing with non-regex (exact match) use cases
C
21
star
7

droiuby

App development platform for Android using Ruby. Develop android apps rapidly using the programming language that we all love, Ruby. Leveraging the JRuby runtime, Droiuby is designed as an open environment where you can use your own framework to develop apps.
Ruby
19
star
8

android_internals_handbook

An unofficial and crazy guide to hacking (in a good way) android on YOUR device. This includes rooting, custom roms and various other tweaks.
9
star
9

device_samsung_gtexslte

Device tree for the Samsung Galaxy Tab A 7.0 (2016) SM-T285
C
8
star
10

GiNote

A Notetaking app for android
Java
7
star
11

ruby-rpg-engine

A ruby based text commandline RPG engine based on DnD 5e rules
Ruby
6
star
12

smt285_data_scripts

Various shell scripts for hacking the Samsung Galaxy Tab A 7.0 SM-T285
Shell
6
star
13

sinatra-proxy

A simple sinatra based forward and reverse proxy
Ruby
5
star
14

ender3-skr1.3-hermera

Marlin 2.0 bugfix with skr 1.3 w/ e3d hermera and tmc2209/2208 mods
C++
4
star
15

kernel_9832_7

C
3
star
16

droiuby-doo

Easy android development using ruby
Ruby
3
star
17

resque-fifo-queue

FIFO queue with consistent hashing on top of resque
Ruby
3
star
18

kernel_7730

C
3
star
19

pinnaclesports

ruby client for the pinnaclesports api (unofficial)
Ruby
3
star
20

kernel_samsung_gtexslte

STOCK Kernel sources for the SM-T285 with SELinux patches for Android 6.0 support
C
3
star
21

vendor_sprd_9830

C
2
star
22

atom-droiuby

Droiuby package for the github atom editor
CoffeeScript
1
star
23

oisems

Open Internetworked Secure Message Service (OISEMS) - An attempt to build a secure peer to peer robust message framework using off the shelf WIFI routers and/or bluetooth devices
Ruby
1
star
24

appstash

Appstash for android
Ruby
1
star
25

torii-globe

Globe OAuth2 provider for ember torii
JavaScript
1
star
26

ember-lenddo

ember add-on to add the lenddo button as a component to your Ember CLI app
JavaScript
1
star
27

hardware_sprd

C
1
star
28

dex-os

The DEX Extensible Operating System. An educational operating system I developed during my college days.
C
1
star
29

jetson-voice-assistant

A real time AI voice assistant for the Jetson Orin Nano. Fully self-contained, runs the Mistral 7B locally, setup a dedicated large language model home assistant using your Jetson Nano.
Python
1
star