tangram 0.1.0.pre.alpha.7 → 0.1.1
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 +4 -4
- data/README.md +52 -1
- data/examples/monitor/heart-disease.tangram +0 -0
- data/examples/monitor/main.rb +52 -0
- data/examples/predict/heart-disease.tangram +0 -0
- data/examples/predict/main.rb +30 -0
- data/lib/tangram/libtangram-0.1.0-linux-x86_64.so +0 -0
- data/lib/tangram/libtangram-0.1.0-macos-x86_64.dylib +0 -0
- data/lib/tangram/libtangram.rb +4 -4
- data/lib/tangram/model.rb +92 -8
- data/lib/tangram/{tangram-0.1.0-alpha.4-windows-x86_64.dll → tangram-0.1.0-windows-x86_64.dll} +0 -0
- data/tangram.gemspec +1 -2
- metadata +11 -8
- data/examples/heart-disease/main.rb +0 -25
- data/lib/tangram/libtangram-0.1.0-alpha.4-linux-x86_64.so +0 -0
- data/lib/tangram/libtangram-0.1.0-alpha.4-macos-x86_64.dylib +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e622eeb9d9cd659b75acbf6dc167a0685a5111ab35a434c888c497de99faf742
|
4
|
+
data.tar.gz: 91c21be6670a9e3d3072648dba57b570a4cc313958af8a5a16ee7588f461c9b9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5079553215b584e04826f4e522e5b47587711e7493c389f587ec2202acc33a72a9f769d1155e11f746bbb69320a4d2a3bee587701b33877a558ddf467d7d967c
|
7
|
+
data.tar.gz: 0d86be9c1a11e988cbcd6549ae9eb55c0cde9126ab85f1f99f23f796100c158ee32401fe92e0a63b28bacee114cf3a15f2cf0af6b265bf3dbd03717ed44bd86d
|
data/README.md
CHANGED
@@ -1 +1,52 @@
|
|
1
|
-
#
|
1
|
+
# Tangram + Ruby
|
2
|
+
|
3
|
+
- [Watch the Video](https://www.tangramhq.com)
|
4
|
+
- [Follow the Getting Started guide](https://www.tangramhq.com/docs/getting-started)
|
5
|
+
|
6
|
+
The Tangram Ruby library makes it easy to make predictions with your Tangram machine learning model from Ruby.
|
7
|
+
|
8
|
+
## Usage
|
9
|
+
|
10
|
+
```
|
11
|
+
$ gem install tangram
|
12
|
+
```
|
13
|
+
|
14
|
+
```ruby
|
15
|
+
require 'tangram'
|
16
|
+
|
17
|
+
model = Tangram::Model.from_file('./heart-disease.tangram')
|
18
|
+
|
19
|
+
input = {
|
20
|
+
age: 63,
|
21
|
+
gender: 'male',
|
22
|
+
# ...
|
23
|
+
}
|
24
|
+
|
25
|
+
output = model.predict(input)
|
26
|
+
|
27
|
+
puts(output)
|
28
|
+
```
|
29
|
+
|
30
|
+
For more information, follow the [Getting Started guide](https://www.tangramhq.com/docs/getting-started).
|
31
|
+
|
32
|
+
## Examples
|
33
|
+
|
34
|
+
This repo contains two examples, [examples/predict]([examples/predict]) and [examples/monitor](examples/monitor).
|
35
|
+
|
36
|
+
### Predict
|
37
|
+
|
38
|
+
This example demonstrates loading a model from a `.tangram` file and making a prediction.
|
39
|
+
|
40
|
+
### Monitor
|
41
|
+
|
42
|
+
This example demonstrates logging predictions and true values to the Tangram reporting and monitoring app. Before running the example, upload the `heart-disease.tangram` file in [examples/monitor/heart-disease.tangram](examples/monitor/heart-disease.tangram) to either https://app.tangramhq.com or your private instance. Then generate a token by clicking the "Tokens" link on the right side of the top bar.
|
43
|
+
|
44
|
+
Then you can run the example:
|
45
|
+
|
46
|
+
```
|
47
|
+
$ TANGRAM_TOKEN=<YOUR TOKEN HERE> ruby examples/monitor/main.rb
|
48
|
+
```
|
49
|
+
|
50
|
+
Now if you refresh the production stats or production metrics tabs for the model you uploaded, you should see predictions and true values being reported.
|
51
|
+
|
52
|
+
For more information on reporting and monitoring, follow the [Getting Started guide](https://www.tangramhq.com/docs/getting-started).
|
Binary file
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'tangram'
|
2
|
+
|
3
|
+
# Go to https://app.tangramhq.com or your private instance, upload the `heart-disease.tangram` file adjacent to this file, generate a token, and pass it as an environment variable to this script.
|
4
|
+
token = ENV['TANGRAM_TOKEN']
|
5
|
+
if token.nil?
|
6
|
+
raise 'TANGRAM_TOKEN environment variable not set'
|
7
|
+
end
|
8
|
+
base_url = ENV['TANGRAM_BASE_URL'] || 'https://app.tangramhq.com'
|
9
|
+
|
10
|
+
# Get the path to the `.tangram` file.
|
11
|
+
model_path = File.join(File.dirname(__FILE__), 'heart-disease.tangram')
|
12
|
+
# Load the model from the file and set the Tangram API token and base url.
|
13
|
+
model = Tangram::Model.from_file(model_path, token: token, base_url: base_url)
|
14
|
+
|
15
|
+
# Create an example input matching the schema of the CSV file the model was trained on. Here the data is just hard-coded, but in your application you will probably get this from a database or user input.
|
16
|
+
input = {
|
17
|
+
age: 63,
|
18
|
+
gender: 'male',
|
19
|
+
chest_pain: 'typical angina',
|
20
|
+
resting_blood_pressure: 145,
|
21
|
+
cholesterol: 233,
|
22
|
+
fasting_blood_sugar_greater_than_120: 'true',
|
23
|
+
resting_ecg_result: 'probable or definite left ventricular hypertrophy',
|
24
|
+
exercise_max_heart_rate: 150,
|
25
|
+
exercise_induced_angina: 'no',
|
26
|
+
exercise_st_depression: 2.3,
|
27
|
+
exercise_st_slope: 'downsloping',
|
28
|
+
fluoroscopy_vessels_colored: 0,
|
29
|
+
thallium_stress_test: 'fixed defect',
|
30
|
+
}
|
31
|
+
|
32
|
+
# Make the prediction using a custom threshold chosen on the "Tuning" page of the Tangram reporting and monitoring app.
|
33
|
+
options = { threshold: 0.25 }
|
34
|
+
output = model.predict(input, options: options)
|
35
|
+
|
36
|
+
# Make the prediction using a custom threshold chosen on the "Tuning" page of the Tangram reporting and monitoring app.
|
37
|
+
puts('Input:', input)
|
38
|
+
puts('Output:', output)
|
39
|
+
|
40
|
+
# Log the prediction. This will allow us to view production stats in the Tangram reporting and monitoring app.
|
41
|
+
model.log_prediction(
|
42
|
+
identifier: 'John Doe',
|
43
|
+
options: options,
|
44
|
+
input: input,
|
45
|
+
output: output,
|
46
|
+
)
|
47
|
+
|
48
|
+
# Later on, if we get an official diagnosis for the patient, we can log the true value for the prior prediction. Make sure to match the `identifier` from the former prediction.
|
49
|
+
model.log_true_value(
|
50
|
+
identifier: 'John Doe',
|
51
|
+
true_value: 'Positive',
|
52
|
+
)
|
Binary file
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'tangram'
|
2
|
+
|
3
|
+
# Get the path to the .tangram file.
|
4
|
+
model_path = File.join(File.dirname(__FILE__), 'heart-disease.tangram')
|
5
|
+
# Load the model from the file.
|
6
|
+
model = Tangram::Model.from_file(model_path)
|
7
|
+
|
8
|
+
# Create an example input matching the schema of the CSV file the model was trained on. Here the data is just hard-coded, but in your application you will probably get this from a database or user input.
|
9
|
+
input = {
|
10
|
+
age: 63,
|
11
|
+
gender: 'male',
|
12
|
+
chest_pain: 'typical angina',
|
13
|
+
resting_blood_pressure: 145,
|
14
|
+
cholesterol: 233,
|
15
|
+
fasting_blood_sugar_greater_than_120: 'true',
|
16
|
+
resting_ecg_result: 'probable or definite left ventricular hypertrophy',
|
17
|
+
exercise_max_heart_rate: 150,
|
18
|
+
exercise_induced_angina: 'no',
|
19
|
+
exercise_st_depression: 2.3,
|
20
|
+
exercise_st_slope: 'downsloping',
|
21
|
+
fluoroscopy_vessels_colored: 0,
|
22
|
+
thallium_stress_test: 'fixed defect',
|
23
|
+
}
|
24
|
+
|
25
|
+
# Make the prediction!
|
26
|
+
output = model.predict(input)
|
27
|
+
|
28
|
+
# Print out the input and output.
|
29
|
+
puts('Input:', input)
|
30
|
+
puts('Output:', output)
|
Binary file
|
Binary file
|
data/lib/tangram/libtangram.rb
CHANGED
@@ -5,13 +5,13 @@ module LibTangram
|
|
5
5
|
cpu = RbConfig::CONFIG['host_cpu']
|
6
6
|
os = RbConfig::CONFIG['host_os']
|
7
7
|
if cpu == 'x86_64' and os =~ /linux/
|
8
|
-
library_name = 'libtangram-0.1.0-
|
8
|
+
library_name = 'libtangram-0.1.0-linux-x86_64.so'
|
9
9
|
elsif cpu == 'x86_64' and os =~ /darwin/
|
10
|
-
library_name = 'libtangram-0.1.0-
|
10
|
+
library_name = 'libtangram-0.1.0-macos-x86_64.dylib'
|
11
11
|
elsif cpu == 'x86_64' and os =~ /mingw/
|
12
|
-
library_name = 'tangram-0.1.0-
|
12
|
+
library_name = 'tangram-0.1.0-windows-x86_64.dll'
|
13
13
|
else
|
14
|
-
raise 'tangram-ruby does not yet support your combination of operating system and CPU architecture.'
|
14
|
+
raise 'tangram-ruby does not yet support your combination of operating system and CPU architecture. Want support for your platform? Get in touch at help@tangramhq.com.'
|
15
15
|
end
|
16
16
|
extend FFI::Library
|
17
17
|
ffi_lib File.expand_path("#{library_name}", __dir__)
|
data/lib/tangram/model.rb
CHANGED
@@ -1,18 +1,23 @@
|
|
1
|
-
require 'tangram/libtangram'
|
2
1
|
require 'json'
|
2
|
+
require 'net/http'
|
3
|
+
require 'tangram/libtangram'
|
3
4
|
|
4
5
|
module Tangram
|
5
6
|
class Model
|
6
|
-
|
7
|
+
|
8
|
+
def self.from_file(model_path, base_url: nil, token: nil)
|
7
9
|
model_data = IO.binread(model_path)
|
8
|
-
self.from_data(model_data)
|
10
|
+
self.from_data(model_data, base_url: base_url, token: token)
|
9
11
|
end
|
10
12
|
|
11
|
-
def self.from_data(model_data)
|
12
|
-
self.new(model_data)
|
13
|
+
def self.from_data(model_data, base_url: nil, token: nil)
|
14
|
+
self.new(model_data, base_url: base_url, token: token)
|
13
15
|
end
|
14
16
|
|
15
|
-
def initialize(model_data)
|
17
|
+
def initialize(model_data, base_url: nil, token: nil)
|
18
|
+
@base_url = base_url.nil? ? 'https://app.tangramhq.com' : base_url
|
19
|
+
@token = token
|
20
|
+
@log_queue = []
|
16
21
|
model_ptr = FFI::MemoryPointer.new(:pointer)
|
17
22
|
result = LibTangram.tangram_model_load(model_data, model_data.size, model_ptr)
|
18
23
|
if result != 0
|
@@ -21,7 +26,18 @@ module Tangram
|
|
21
26
|
@model = FFI::AutoPointer.new(model_ptr.read_pointer, LibTangram.method(:tangram_model_free))
|
22
27
|
end
|
23
28
|
|
24
|
-
def
|
29
|
+
def id()
|
30
|
+
id_ptr = FFI::MemoryPointer.new(:pointer)
|
31
|
+
result = LibTangram.tangram_model_id(@model, id_ptr)
|
32
|
+
if result != 0
|
33
|
+
raise 'tangram error'
|
34
|
+
end
|
35
|
+
id = id_ptr.read_pointer.read_string.force_encoding('utf-8')
|
36
|
+
LibTangram.tangram_string_free(id_ptr.read_pointer)
|
37
|
+
id
|
38
|
+
end
|
39
|
+
|
40
|
+
def task()
|
25
41
|
task_ptr = FFI::MemoryPointer.new(:pointer)
|
26
42
|
result = LibTangram.tangram_model_task(@model, task_ptr)
|
27
43
|
if result != 0
|
@@ -32,7 +48,7 @@ module Tangram
|
|
32
48
|
task
|
33
49
|
end
|
34
50
|
|
35
|
-
def predict(input, options)
|
51
|
+
def predict(input, options: nil)
|
36
52
|
input = JSON.unparse(input)
|
37
53
|
unless options.nil?
|
38
54
|
options = JSON.unparse(options)
|
@@ -49,5 +65,73 @@ module Tangram
|
|
49
65
|
LibTangram.tangram_string_free(output_ptr.read_pointer)
|
50
66
|
output
|
51
67
|
end
|
68
|
+
|
69
|
+
def log_prediction(identifier:, options: nil, input:, output:)
|
70
|
+
self.log_event({
|
71
|
+
type: 'prediction',
|
72
|
+
modelId: self.id,
|
73
|
+
identifier: identifier,
|
74
|
+
options: options,
|
75
|
+
input: input,
|
76
|
+
output: output,
|
77
|
+
})
|
78
|
+
end
|
79
|
+
|
80
|
+
def enqueue_log_prediction(identifier:, options: nil, input:, output:)
|
81
|
+
self.log_queue.push({
|
82
|
+
type: 'prediction',
|
83
|
+
modelId: self.id,
|
84
|
+
identifier: identifier,
|
85
|
+
options: options,
|
86
|
+
input: input,
|
87
|
+
output: output,
|
88
|
+
})
|
89
|
+
end
|
90
|
+
|
91
|
+
def log_true_value(identifier:, true_value:)
|
92
|
+
self.log_event({
|
93
|
+
type: 'true_value',
|
94
|
+
modelId: self.id,
|
95
|
+
identifier: identifier,
|
96
|
+
trueValue: true_value,
|
97
|
+
})
|
98
|
+
end
|
99
|
+
|
100
|
+
def enqueue_log_true_value(identifier:, true_value:)
|
101
|
+
self.log_queue.push({
|
102
|
+
type: 'true_value',
|
103
|
+
modelId: self.id,
|
104
|
+
identifier: identifier,
|
105
|
+
trueValue: true_value,
|
106
|
+
})
|
107
|
+
end
|
108
|
+
|
109
|
+
def flush_log_queue()
|
110
|
+
self.log_events(@log_queue)
|
111
|
+
@log_queue = []
|
112
|
+
end
|
113
|
+
|
114
|
+
def log_event(event)
|
115
|
+
self.log_events([event])
|
116
|
+
end
|
117
|
+
|
118
|
+
def log_events(events)
|
119
|
+
if @token.nil?
|
120
|
+
raise 'Tangram cannot log events without a token. Make sure to pass a token to the model constructor.'
|
121
|
+
end
|
122
|
+
headers = {
|
123
|
+
'Content-Type': 'application/json',
|
124
|
+
'Authorization': 'Bearer ' + @token,
|
125
|
+
}
|
126
|
+
uri = URI(@base_url + '/api/track')
|
127
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
128
|
+
request = Net::HTTP::Post.new(uri.request_uri, headers)
|
129
|
+
request.body = events.to_json
|
130
|
+
response = http.request(request)
|
131
|
+
unless response.kind_of? Net::HTTPSuccess
|
132
|
+
raise response
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
52
136
|
end
|
53
137
|
end
|
data/lib/tangram/{tangram-0.1.0-alpha.4-windows-x86_64.dll → tangram-0.1.0-windows-x86_64.dll}
RENAMED
Binary file
|
data/tangram.gemspec
CHANGED
@@ -1,7 +1,6 @@
|
|
1
1
|
Gem::Specification.new do |s|
|
2
2
|
s.name = "tangram"
|
3
|
-
s.version = "0.1.
|
4
|
-
s.date = "2019-11-22"
|
3
|
+
s.version = "0.1.1"
|
5
4
|
s.summary = "Tangram for Ruby"
|
6
5
|
s.description = "Make predictions with a Tangram model from your Ruby app. Learn more at https://www.tangramhq.com/."
|
7
6
|
s.authors = ["Tangram"]
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: tangram
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Tangram
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2020-01-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: ffi
|
@@ -32,13 +32,16 @@ extensions: []
|
|
32
32
|
extra_rdoc_files: []
|
33
33
|
files:
|
34
34
|
- README.md
|
35
|
-
- examples/heart-disease
|
35
|
+
- examples/monitor/heart-disease.tangram
|
36
|
+
- examples/monitor/main.rb
|
37
|
+
- examples/predict/heart-disease.tangram
|
38
|
+
- examples/predict/main.rb
|
36
39
|
- lib/tangram.rb
|
37
|
-
- lib/tangram/libtangram-0.1.0-
|
38
|
-
- lib/tangram/libtangram-0.1.0-
|
40
|
+
- lib/tangram/libtangram-0.1.0-linux-x86_64.so
|
41
|
+
- lib/tangram/libtangram-0.1.0-macos-x86_64.dylib
|
39
42
|
- lib/tangram/libtangram.rb
|
40
43
|
- lib/tangram/model.rb
|
41
|
-
- lib/tangram/tangram-0.1.0-
|
44
|
+
- lib/tangram/tangram-0.1.0-windows-x86_64.dll
|
42
45
|
- scripts/build
|
43
46
|
- tangram.gemspec
|
44
47
|
homepage: http://rubygems.org/gems/tangram
|
@@ -56,9 +59,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
56
59
|
version: '0'
|
57
60
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
58
61
|
requirements:
|
59
|
-
- - "
|
62
|
+
- - ">="
|
60
63
|
- !ruby/object:Gem::Version
|
61
|
-
version:
|
64
|
+
version: '0'
|
62
65
|
requirements: []
|
63
66
|
rubygems_version: 3.0.6
|
64
67
|
signing_key:
|
@@ -1,25 +0,0 @@
|
|
1
|
-
require 'tangram'
|
2
|
-
|
3
|
-
model = Tangram::Model.from_file('./heart-disease.tangram')
|
4
|
-
|
5
|
-
puts model.task
|
6
|
-
|
7
|
-
input = {
|
8
|
-
age: 63,
|
9
|
-
gender: 'male',
|
10
|
-
chest_pain: 'typical angina',
|
11
|
-
resting_blood_pressure: 145,
|
12
|
-
cholesterol: 233,
|
13
|
-
fasting_blood_sugar_greater_than_120: 'true',
|
14
|
-
resting_ecg_result: 'probable or definite left ventricular hypertrophy',
|
15
|
-
exercise_max_heart_rate: 150,
|
16
|
-
exercise_induced_angina: 'no',
|
17
|
-
exercise_st_depression: 2.3,
|
18
|
-
exercise_st_slope: 'downsloping',
|
19
|
-
fluoroscopy_vessels_colored: 0,
|
20
|
-
thallium_stress_test: 'fixed defect',
|
21
|
-
}
|
22
|
-
|
23
|
-
output = model.predict(input)
|
24
|
-
|
25
|
-
puts('Prediction:', output['className'])
|
Binary file
|
Binary file
|