tangram 0.1.3 → 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +3 -0
- data/Gemfile.lock +15 -0
- data/LICENSE +1 -1
- data/README.md +8 -28
- data/docs/Tangram.html +127 -0
- data/docs/Tangram/BagOfWordsCosineSimilarityFeatureContribution.html +507 -0
- data/docs/Tangram/BagOfWordsFeatureContribution.html +507 -0
- data/docs/Tangram/Bigram.html +363 -0
- data/docs/Tangram/BinaryClassificationPredictOutput.html +493 -0
- data/docs/Tangram/FeatureContributions.html +435 -0
- data/docs/Tangram/IdentityFeatureContribution.html +435 -0
- data/docs/Tangram/LibTangram.html +161 -0
- data/docs/Tangram/LibTangram/TangramStringView.html +193 -0
- data/docs/Tangram/LoadModelOptions.html +291 -0
- data/docs/Tangram/Model.html +1250 -0
- data/docs/Tangram/MulticlassClassificationPredictOutput.html +565 -0
- data/docs/Tangram/NormalizedFeatureContribution.html +435 -0
- data/docs/Tangram/OneHotEncodedFeatureContribution.html +507 -0
- data/docs/Tangram/PredictOptions.html +421 -0
- data/docs/Tangram/RegressionPredictOutput.html +421 -0
- data/docs/Tangram/Unigram.html +291 -0
- data/docs/Tangram/WordEmbeddingFeatureContribution.html +435 -0
- data/docs/_index.html +317 -0
- data/docs/class_list.html +51 -0
- data/docs/css/common.css +1 -0
- data/docs/css/full_list.css +58 -0
- data/docs/css/style.css +497 -0
- data/docs/file.README.html +106 -0
- data/docs/file_list.html +56 -0
- data/docs/frames.html +17 -0
- data/docs/index.html +106 -0
- data/docs/js/app.js +314 -0
- data/docs/js/full_list.js +216 -0
- data/docs/js/jquery.js +4 -0
- data/docs/method_list.html +595 -0
- data/docs/top-level-namespace.html +110 -0
- data/examples/advanced/Gemfile +2 -0
- data/examples/advanced/Gemfile.lock +15 -0
- data/examples/advanced/README.md +14 -0
- data/examples/advanced/heart_disease.tangram +0 -0
- data/examples/advanced/main.rb +49 -0
- data/examples/basic/Gemfile +2 -0
- data/examples/basic/Gemfile.lock +15 -0
- data/examples/basic/README.md +10 -0
- data/examples/basic/heart_disease.tangram +0 -0
- data/examples/basic/main.rb +29 -0
- data/lib/tangram.rb +1 -1
- data/lib/tangram/libtangram/aarch64-apple-darwin/libtangram.dylib +0 -0
- data/lib/tangram/libtangram/aarch64-unknown-linux-gnu/libtangram.so +0 -0
- data/lib/tangram/libtangram/aarch64-unknown-linux-musl/libtangram.so +0 -0
- data/lib/tangram/libtangram/x86_64-apple-darwin/libtangram.dylib +0 -0
- data/lib/tangram/libtangram/x86_64-pc-windows-msvc/tangram.dll +0 -0
- data/lib/tangram/libtangram/x86_64-unknown-linux-gnu/libtangram.so +0 -0
- data/lib/tangram/libtangram/x86_64-unknown-linux-musl/libtangram.so +0 -0
- data/lib/tangram/tangram.rb +895 -0
- data/scripts/dev +5 -0
- data/scripts/docs +2 -0
- data/scripts/fmt +0 -0
- data/scripts/release +13 -0
- data/tangram.gemspec +10 -10
- metadata +64 -18
- data/examples/monitor/heart-disease.tangram +0 -0
- data/examples/monitor/main.rb +0 -52
- data/examples/predict/heart-disease.tangram +0 -0
- data/examples/predict/main.rb +0 -30
- data/lib/tangram/libtangram-0.1.3-linux-x86_64.so +0 -0
- data/lib/tangram/libtangram-0.1.3-macos-x86_64.dylib +0 -0
- data/lib/tangram/libtangram.rb +0 -24
- data/lib/tangram/model.rb +0 -133
- data/lib/tangram/tangram-0.1.3-windows-x86_64.dll +0 -0
- data/scripts/build +0 -5
@@ -0,0 +1,110 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html>
|
3
|
+
<head>
|
4
|
+
<meta charset="utf-8">
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
6
|
+
<title>
|
7
|
+
Top Level Namespace
|
8
|
+
|
9
|
+
— Documentation by YARD 0.9.26
|
10
|
+
|
11
|
+
</title>
|
12
|
+
|
13
|
+
<link rel="stylesheet" href="css/style.css" type="text/css" />
|
14
|
+
|
15
|
+
<link rel="stylesheet" href="css/common.css" type="text/css" />
|
16
|
+
|
17
|
+
<script type="text/javascript">
|
18
|
+
pathId = "";
|
19
|
+
relpath = '';
|
20
|
+
</script>
|
21
|
+
|
22
|
+
|
23
|
+
<script type="text/javascript" charset="utf-8" src="js/jquery.js"></script>
|
24
|
+
|
25
|
+
<script type="text/javascript" charset="utf-8" src="js/app.js"></script>
|
26
|
+
|
27
|
+
|
28
|
+
</head>
|
29
|
+
<body>
|
30
|
+
<div class="nav_wrap">
|
31
|
+
<iframe id="nav" src="class_list.html?1"></iframe>
|
32
|
+
<div id="resizer"></div>
|
33
|
+
</div>
|
34
|
+
|
35
|
+
<div id="main" tabindex="-1">
|
36
|
+
<div id="header">
|
37
|
+
<div id="menu">
|
38
|
+
|
39
|
+
<a href="_index.html">Index</a> »
|
40
|
+
|
41
|
+
|
42
|
+
<span class="title">Top Level Namespace</span>
|
43
|
+
|
44
|
+
</div>
|
45
|
+
|
46
|
+
<div id="search">
|
47
|
+
|
48
|
+
<a class="full_list_link" id="class_list_link"
|
49
|
+
href="class_list.html">
|
50
|
+
|
51
|
+
<svg width="24" height="24">
|
52
|
+
<rect x="0" y="4" width="24" height="4" rx="1" ry="1"></rect>
|
53
|
+
<rect x="0" y="12" width="24" height="4" rx="1" ry="1"></rect>
|
54
|
+
<rect x="0" y="20" width="24" height="4" rx="1" ry="1"></rect>
|
55
|
+
</svg>
|
56
|
+
</a>
|
57
|
+
|
58
|
+
</div>
|
59
|
+
<div class="clear"></div>
|
60
|
+
</div>
|
61
|
+
|
62
|
+
<div id="content"><h1>Top Level Namespace
|
63
|
+
|
64
|
+
|
65
|
+
|
66
|
+
</h1>
|
67
|
+
<div class="box_info">
|
68
|
+
|
69
|
+
|
70
|
+
|
71
|
+
|
72
|
+
|
73
|
+
|
74
|
+
|
75
|
+
|
76
|
+
|
77
|
+
|
78
|
+
|
79
|
+
</div>
|
80
|
+
|
81
|
+
<h2>Defined Under Namespace</h2>
|
82
|
+
<p class="children">
|
83
|
+
|
84
|
+
|
85
|
+
<strong class="modules">Modules:</strong> <span class='object_link'><a href="Tangram.html" title="Tangram (module)">Tangram</a></span>
|
86
|
+
|
87
|
+
|
88
|
+
|
89
|
+
|
90
|
+
</p>
|
91
|
+
|
92
|
+
|
93
|
+
|
94
|
+
|
95
|
+
|
96
|
+
|
97
|
+
|
98
|
+
|
99
|
+
|
100
|
+
</div>
|
101
|
+
|
102
|
+
<div id="footer">
|
103
|
+
Generated on Thu Jul 15 14:34:33 2021 by
|
104
|
+
<a href="http://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
|
105
|
+
0.9.26 (ruby-2.7.3).
|
106
|
+
</div>
|
107
|
+
|
108
|
+
</div>
|
109
|
+
</body>
|
110
|
+
</html>
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# Advanced
|
2
|
+
|
3
|
+
This example demonstrates logging predictions and true values to the Tangram app. Before running the example, run `tangram app` to start the app running locally, open `http://localhost:8080` in your browser, and upload the file `heart_disease.tangram` to it.
|
4
|
+
|
5
|
+
To run the example:
|
6
|
+
|
7
|
+
```
|
8
|
+
$ bundle
|
9
|
+
$ TANGRAM_URL=http://localhost:8080 ruby main.rb
|
10
|
+
```
|
11
|
+
|
12
|
+
Now if you refresh the production stats or production metrics tabs for the model you uploaded, you should see predictions and true values.
|
13
|
+
|
14
|
+
For more information, [read the docs](https://www.tangram.xyz/docs).
|
Binary file
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'tangram'
|
2
|
+
|
3
|
+
# If you are running the Tangram app on your own server you can pass the URL to it with the TANGRAM_URL environment variable.
|
4
|
+
tangram_url = ENV['TANGRAM_URL'] || 'https://app.tangram.xyz'
|
5
|
+
|
6
|
+
# Get the path to the `.tangram` file.
|
7
|
+
model_path = File.join(File.dirname(__FILE__), 'heart_disease.tangram')
|
8
|
+
# Load the model from the path and set the url where the tangram app is running.
|
9
|
+
options = Tangram::LoadModelOptions.new(tangram_url: tangram_url)
|
10
|
+
model = Tangram::Model.from_path(model_path, options: options)
|
11
|
+
|
12
|
+
# 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.
|
13
|
+
input = {
|
14
|
+
age: 63.0,
|
15
|
+
gender: 'male',
|
16
|
+
chest_pain: 'typical angina',
|
17
|
+
resting_blood_pressure: 145.0,
|
18
|
+
cholesterol: 233.0,
|
19
|
+
fasting_blood_sugar_greater_than_120: 'true',
|
20
|
+
resting_ecg_result: 'probable or definite left ventricular hypertrophy',
|
21
|
+
exercise_max_heart_rate: 150.0,
|
22
|
+
exercise_induced_angina: 'no',
|
23
|
+
exercise_st_depression: 2.3,
|
24
|
+
exercise_st_slope: 'downsloping',
|
25
|
+
fluoroscopy_vessels_colored: '0',
|
26
|
+
thallium_stress_test: 'fixed defect'
|
27
|
+
}
|
28
|
+
|
29
|
+
# Make the prediction using a custom threshold chosen on the "Tuning" page of the Tangram app.
|
30
|
+
options = Tangram::PredictOptions.new(threshold: 0.5, compute_feature_contributions: true)
|
31
|
+
output = model.predict(input, options: options)
|
32
|
+
|
33
|
+
# Make the prediction using a custom threshold chosen on the "Tuning" page of the Tangram app.
|
34
|
+
puts('Input:', input)
|
35
|
+
puts('Output:', output)
|
36
|
+
|
37
|
+
# Log the prediction.
|
38
|
+
model.log_prediction(
|
39
|
+
identifier: '71762b29-2296-4bf9-a1d4-59144d74c9d9',
|
40
|
+
input: input,
|
41
|
+
output: output,
|
42
|
+
options: options
|
43
|
+
)
|
44
|
+
|
45
|
+
# Later on, if we get an official diagnosis for the patient, log the true value. Make sure to match the `identifier`.
|
46
|
+
model.log_true_value(
|
47
|
+
identifier: '71762b29-2296-4bf9-a1d4-59144d74c9d9',
|
48
|
+
true_value: 'Positive'
|
49
|
+
)
|
Binary file
|
@@ -0,0 +1,29 @@
|
|
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 path.
|
6
|
+
model = Tangram::Model.from_path(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.0,
|
11
|
+
gender: 'male',
|
12
|
+
chest_pain: 'typical angina',
|
13
|
+
resting_blood_pressure: 145.0,
|
14
|
+
cholesterol: 233.0,
|
15
|
+
fasting_blood_sugar_greater_than_120: 'true',
|
16
|
+
resting_ecg_result: 'probable or definite left ventricular hypertrophy',
|
17
|
+
exercise_max_heart_rate: 150.0,
|
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 the output.
|
29
|
+
puts('Output:', output.inspect)
|
data/lib/tangram.rb
CHANGED
@@ -1 +1 @@
|
|
1
|
-
require 'tangram/
|
1
|
+
require 'tangram/tangram'
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
@@ -0,0 +1,895 @@
|
|
1
|
+
require 'date'
|
2
|
+
require 'ffi'
|
3
|
+
require 'json'
|
4
|
+
require 'net/http'
|
5
|
+
require 'rbconfig'
|
6
|
+
|
7
|
+
# This is the main module in the `tangram` gem.
|
8
|
+
module Tangram
|
9
|
+
|
10
|
+
# These are the options passed when loading a model.
|
11
|
+
class LoadModelOptions
|
12
|
+
# If you are running the app locally or on your own server, use this field to provide the url to it. If not specified, the default value is https://app.tangram.xyz.
|
13
|
+
attr_reader :tangram_url
|
14
|
+
def initialize(tangram_url:)
|
15
|
+
@tangram_url = tangram_url
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
# These are the options passed to `predict`.
|
20
|
+
class PredictOptions
|
21
|
+
# If your model is a binary classifier, use this field to make predictions using a threshold chosen on the tuning page of the app. The default value is `0.5`.
|
22
|
+
attr_reader :threshold
|
23
|
+
# Computing feature contributions is disabled by default. If you set this field to `true`, you will be able to access the feature contributions with the `feature_contributions` field of the predict output.
|
24
|
+
attr_reader :compute_feature_contributions
|
25
|
+
def initialize(compute_feature_contributions:, threshold: nil)
|
26
|
+
@threshold = threshold
|
27
|
+
@compute_feature_contributions = compute_feature_contributions
|
28
|
+
end
|
29
|
+
def to_json(*args)
|
30
|
+
{'threshold' => @threshold, 'compute_feature_contributions' => @compute_feature_contributions}.to_json(*args)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# `predict` outputs `RegressionPredictOutput` when the model's task is regression.
|
35
|
+
class RegressionPredictOutput
|
36
|
+
# This is the predicted value.
|
37
|
+
attr_reader :value
|
38
|
+
# If computing feature contributions was enabled in the predict options, this value will explain the model's output, showing how much each feature contributed to the output.
|
39
|
+
attr_reader :feature_contributions
|
40
|
+
def initialize(value:, feature_contributions:)
|
41
|
+
@value = value
|
42
|
+
@feature_contributions = feature_contributions
|
43
|
+
end
|
44
|
+
def to_json(*args)
|
45
|
+
{'value' => @value}.to_json(*args)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
# `predict` outputs `BinaryClassificationPredictOutput` when the model's task is binary classification.
|
50
|
+
class BinaryClassificationPredictOutput
|
51
|
+
# This is the name of the predicted class.
|
52
|
+
attr_reader :class_name
|
53
|
+
# This is the probability the model assigned to the predicted class.
|
54
|
+
attr_reader :probability
|
55
|
+
# If computing feature contributions was enabled in the predict options, this value will explain the model's output, showing how much each feature contributed to the output.
|
56
|
+
attr_reader :feature_contributions
|
57
|
+
def initialize(class_name:, probability:, feature_contributions:)
|
58
|
+
@class_name = class_name
|
59
|
+
@probability = probability
|
60
|
+
@feature_contributions = feature_contributions
|
61
|
+
end
|
62
|
+
def to_json(*args)
|
63
|
+
{'class_name' => @class_name, "probability" => @probability}.to_json(*args)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
# `predict` outputs `MulticlassClassificationPredictOutput` when the model's task is multiclass classification.
|
68
|
+
class MulticlassClassificationPredictOutput
|
69
|
+
# This is the name of the predicted class.
|
70
|
+
attr_reader :class_name
|
71
|
+
# This is the probability the model assigned to the predicted class.
|
72
|
+
attr_reader :probability
|
73
|
+
# This value maps from class names to the probability the model assigned to each class.
|
74
|
+
attr_reader :probabilities
|
75
|
+
# If computing feature contributions was enabled in the predict options, this value will explain the model's output, showing how much each feature contributed to the output. This value maps from class names to `FeatureContributions` values for each class. The class with the `FeatureContributions` value with the highest `output_value` is the predicted class.
|
76
|
+
attr_reader :feature_contributions
|
77
|
+
def initialize(class_name:, probability:, probabilities:, feature_contributions:)
|
78
|
+
@class_name = class_name
|
79
|
+
@probability = probability
|
80
|
+
@probabilities = probabilities
|
81
|
+
@feature_contributions = feature_contributions
|
82
|
+
end
|
83
|
+
def to_json(*args)
|
84
|
+
{'class_name' => @class_name, "probability" => @probability, "probabilities" => @probabilities}.to_json(*args)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
# This is a description of the feature contributions for the prediction if the task is regression or binary classification, or for a single class if the task is multiclass classification.
|
89
|
+
class FeatureContributions
|
90
|
+
# This is the value the model would output if all features had baseline values.
|
91
|
+
attr_reader :baseline
|
92
|
+
# This is the value the model output. Any difference from the `baseline_value` is because of the deviation of the features from their baseline values.
|
93
|
+
attr_reader :output
|
94
|
+
# This list will contain one entry for each of the model's features. Note that features are computed from columns, so there will likely be more features than columns.
|
95
|
+
attr_reader :entries
|
96
|
+
def initialize(baseline:, output:, entries:)
|
97
|
+
@baseline = baseline
|
98
|
+
@output = output
|
99
|
+
@entries = entries
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
# This describes the contribution of a feature from an identity feature group.
|
104
|
+
class IdentityFeatureContribution
|
105
|
+
# This is the name of the source column for the feature group.
|
106
|
+
attr_reader :column_name
|
107
|
+
# This is the value of the feature.
|
108
|
+
attr_reader :feature_value
|
109
|
+
# This is the amount that the feature contributed to the output.
|
110
|
+
attr_reader :feature_contribution_value
|
111
|
+
def initialize(column_name:, feature_contribution_value:, feature_value:)
|
112
|
+
@column_name = column_name
|
113
|
+
@feature_value = feature_value
|
114
|
+
@feature_contribution_value = feature_contribution_value
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
# This describes the contribution of a feature from a normalized feature group.
|
119
|
+
class NormalizedFeatureContribution
|
120
|
+
# This is the name of the source column for the feature group.
|
121
|
+
attr_reader :column_name
|
122
|
+
# This is the value of the feature.
|
123
|
+
attr_reader :feature_value
|
124
|
+
# This is the amount that the feature contributed to the output.
|
125
|
+
attr_reader :feature_contribution_value
|
126
|
+
def initialize(column_name:, feature_value:, feature_contribution_value:)
|
127
|
+
@column_name = column_name
|
128
|
+
@feature_value = feature_value
|
129
|
+
@feature_contribution_value = feature_contribution_value
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
# This describes the contribution of a feature from a one hot encoded feature group.
|
134
|
+
class OneHotEncodedFeatureContribution
|
135
|
+
# This is the name of the source column for the feature group.
|
136
|
+
attr_reader :column_name
|
137
|
+
# This is the enum variant the feature indicates the presence of.
|
138
|
+
attr_reader :variant
|
139
|
+
# This is the value of the feature.
|
140
|
+
attr_reader :feature_value
|
141
|
+
# This is the amount that the feature contributed to the output.b
|
142
|
+
attr_reader :feature_contribution_value
|
143
|
+
def initialize(column_name:, variant:, feature_contribution_value:, feature_value:)
|
144
|
+
@column_name = column_name
|
145
|
+
@variant = variant
|
146
|
+
@feature_contribution_value = feature_contribution_value
|
147
|
+
@feature_value = feature_value
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
# This describes the contribution of a feature from a bag of words feature group.
|
152
|
+
class BagOfWordsFeatureContribution
|
153
|
+
# This is the name of the source column for the feature group.
|
154
|
+
attr_reader :column_name
|
155
|
+
# This is the ngram for the feature.
|
156
|
+
attr_reader :ngram
|
157
|
+
# This is the value of the feature.
|
158
|
+
attr_reader :feature_value
|
159
|
+
# This is the amount that the feature contributed to the output.
|
160
|
+
attr_reader :feature_contribution_value
|
161
|
+
def initialize(column_name:, ngram:, feature_contribution_value:, feature_value:)
|
162
|
+
@column_name = column_name
|
163
|
+
@ngram = ngram
|
164
|
+
@feature_contribution_value = feature_contribution_value
|
165
|
+
@feature_value = feature_value
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
# This describes a unigram ngram.
|
170
|
+
class Unigram
|
171
|
+
# This is the token.
|
172
|
+
attr_reader :token
|
173
|
+
def initialize(token)
|
174
|
+
@token = token
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
# This describes a bigram ngram.
|
179
|
+
class Bigram
|
180
|
+
# This is the first token in the bigram.
|
181
|
+
attr_reader :token_a
|
182
|
+
# This is the second token in the bigram.
|
183
|
+
attr_reader :token_b
|
184
|
+
def initialize(token_a:, token_b:)
|
185
|
+
@token_a = token_a
|
186
|
+
@token_b = token_b
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
# This describes the contribution of a feature from a bag of words cosine similarity feature group.
|
191
|
+
class BagOfWordsCosineSimilarityFeatureContribution
|
192
|
+
# This is the name of the source column a for the feature group.
|
193
|
+
attr_reader :column_name_a
|
194
|
+
# This is the name of the source column b for the feature group.
|
195
|
+
attr_reader :column_name_b
|
196
|
+
# This is the value of the feature.
|
197
|
+
attr_reader :feature_value
|
198
|
+
# This is the amount that the feature contributed to the output.
|
199
|
+
attr_reader :feature_contribution_value
|
200
|
+
def initialize(column_name_a:, column_name_b:, feature_contribution_value:, feature_value:)
|
201
|
+
@column_name_a = column_name_a
|
202
|
+
@column_name_b = column_name_b
|
203
|
+
@feature_contribution_value = feature_contribution_value
|
204
|
+
@feature_value = feature_value
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
# This describes the contribution of a feature from a word embedding feature group.
|
209
|
+
class WordEmbeddingFeatureContribution
|
210
|
+
# This is the name of the source column for the feature group.
|
211
|
+
attr_reader :column_name
|
212
|
+
# This is the index of the feature in the word embedding.
|
213
|
+
attr_reader :value_index
|
214
|
+
# This is the amount that the feature contributed to the output.
|
215
|
+
attr_reader :feature_contribution_value
|
216
|
+
def initialize(column_name:, value_index:, feature_contribution_value:)
|
217
|
+
@column_name = column_name
|
218
|
+
@value_index = value_index
|
219
|
+
@feature_contribution_value = feature_contribution_value
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
223
|
+
# Use this class to load a model, make predictions, and log events to the app.
|
224
|
+
class Model
|
225
|
+
# Load a model from the `.tangram` file at `path`.
|
226
|
+
# @param path [String] The path to the `.tangram` file.
|
227
|
+
# @param options [LoadModelOptions] The options to use when loading the model.
|
228
|
+
# @return [Model]
|
229
|
+
def self.from_path(path, options: nil)
|
230
|
+
c_model = FFI::MemoryPointer.new(:pointer)
|
231
|
+
c_err = LibTangram.tangram_model_from_path(path, c_model)
|
232
|
+
unless c_err.null?
|
233
|
+
c_err = FFI::AutoPointer.new(c_err, LibTangram.method(:tangram_error_delete))
|
234
|
+
c_error_s = LibTangram::TangramStringView.new
|
235
|
+
LibTangram.tangram_error_get_message(c_err, c_error_s)
|
236
|
+
raise c_error_s.into_string
|
237
|
+
end
|
238
|
+
new(c_model, options: options)
|
239
|
+
end
|
240
|
+
|
241
|
+
# Load a model from bytes instead of a file. You should use this only if you already have a `.tangram` loaded into memory. Otherwise, use `Model.from_path`, which is faster because it memory maps the file.
|
242
|
+
# @param bytes [String] The bytes for the .tangram model.
|
243
|
+
# @param options [LoadModelOptions] The options to use when loading the model.
|
244
|
+
# @return [Model]
|
245
|
+
def self.from_bytes(bytes, options: nil)
|
246
|
+
c_model = FFI::MemoryPointer.new(:pointer)
|
247
|
+
c_err = LibTangram.tangram_model_from_bytes(bytes, bytes.size, c_model)
|
248
|
+
unless err.null?
|
249
|
+
c_err = FFI::AutoPointer.new(c_err, LibTangram.method(:tangram_error_delete))
|
250
|
+
c_error_s = LibTangram::TangramStringView.new
|
251
|
+
LibTangram.tangram_error_get_message(c_err, c_error_s)
|
252
|
+
raise errors.into_string
|
253
|
+
end
|
254
|
+
new(c_model, options: options)
|
255
|
+
end
|
256
|
+
|
257
|
+
def initialize(c_model, options: nil)
|
258
|
+
@tangram_url = options&.tangram_url.nil? ? 'https://app.tangram.xyz' : options&.tangram_url
|
259
|
+
@log_queue = []
|
260
|
+
@model = FFI::AutoPointer.new(c_model.read_pointer, LibTangram.method(:tangram_model_delete))
|
261
|
+
end
|
262
|
+
|
263
|
+
# Retrieve the model's id.
|
264
|
+
def id
|
265
|
+
c_id = LibTangram::TangramStringView.new
|
266
|
+
LibTangram.tangram_model_get_id(@model, c_id)
|
267
|
+
c_id.into_string
|
268
|
+
end
|
269
|
+
|
270
|
+
# Make a prediction!
|
271
|
+
# @param input [Array<Hash{String, Symbol => String, Number}>, Hash{String, Symbol => String, Number}] A predict input is either a single predict input which is a map from symbols or strings to strings or floats or an array of such maps. The keys should match the columns in the CSV file you trained your model with.
|
272
|
+
# @param options [PredictOptions] These are the predict options.
|
273
|
+
# @return [Array<RegressionPredictOutput, BinaryClassificationPredictOutput, MulticlassClassificationPredictOutput>, RegressionPredictOutput, BinaryClassificationPredictOutput, MulticlassClassificationPredictOutput]. Return a single output if `input` was a single input, or an array if `input` was an array of `input`s.
|
274
|
+
def predict(input, options: nil)
|
275
|
+
is_array = input.is_a?(Array)
|
276
|
+
input = is_array ? input : [input]
|
277
|
+
c_input_vec = new_predict_input_vec(input)
|
278
|
+
c_options = new_predict_options(options)
|
279
|
+
c_output_vec = FFI::MemoryPointer.new(:pointer)
|
280
|
+
c_error = LibTangram.tangram_model_predict(@model, c_input_vec, c_options, c_output_vec)
|
281
|
+
raise 'tangram error' unless c_error.null?
|
282
|
+
c_output_vec = FFI::AutoPointer.new(c_output_vec.read_pointer, LibTangram.method(:tangram_predict_output_vec_delete))
|
283
|
+
output = predict_output_vec_from_tangram_predict_output_vec(c_output_vec)
|
284
|
+
is_array ? output : output[0]
|
285
|
+
end
|
286
|
+
|
287
|
+
# Send a prediction event to the app. If you want to batch events, you can use `enqueue_log_prediction` instead.
|
288
|
+
# @param identifier [String, Number] This is a unique identifier for the prediction, which will associate it with a true value event and allow you to look it up in the app.
|
289
|
+
# @param input [Hash{String, Symbol => String, Number}] A single `PredictInput`.
|
290
|
+
# @param output [PredictOutput] A single `PredictOutput`.
|
291
|
+
# @param options [PredictOptions] This is the same `predictOptions` value that you passed to `predict`.
|
292
|
+
def log_prediction(identifier:, input:, output:, options: nil)
|
293
|
+
event = prediction_event(
|
294
|
+
identifier: identifier,
|
295
|
+
input: input,
|
296
|
+
output: output,
|
297
|
+
options: options
|
298
|
+
)
|
299
|
+
log_event(event)
|
300
|
+
end
|
301
|
+
|
302
|
+
# Add a prediction event to the queue. Remember to call `flush_log_queue` at a later point to send the event to the app.
|
303
|
+
# @param identifier [String, Number] This is a unique identifier for the prediction, which will associate it with a true value event and allow you to look it up in the app.
|
304
|
+
# @param input [Hash{String, Symbol => String, Number}] A single `PredictInput`.
|
305
|
+
# @param output [PredictOutput] A single `PredictOutput`.
|
306
|
+
# @param options [PredictOptions] This is the same `predictOptions` value that you passed to `predict`.
|
307
|
+
def enqueue_log_prediction(identifier:, input:, output:, options: nil)
|
308
|
+
event = prediction_event(
|
309
|
+
identifier: identifier,
|
310
|
+
input: input,
|
311
|
+
output: output,
|
312
|
+
options: options
|
313
|
+
)
|
314
|
+
log_queue.push(event)
|
315
|
+
end
|
316
|
+
|
317
|
+
# Send a true value event to the app. If you want to batch events, you can use `enqueue_log_true_value` instead.
|
318
|
+
# @param identifier [String, Number] This is a unique identifier for the prediction, which will associate it with a true value event and allow you to look it up in the app.
|
319
|
+
# @param true_value [String, Number] This is the true value for the prediction.
|
320
|
+
def log_true_value(identifier:, true_value:)
|
321
|
+
event = true_value_event(
|
322
|
+
identifier: identifier,
|
323
|
+
true_value: true_value
|
324
|
+
)
|
325
|
+
log_event(event)
|
326
|
+
end
|
327
|
+
|
328
|
+
# Add a true value event to the queue. Remember to call `flush_log_queue` at a later point to send the event to the app.
|
329
|
+
# @param identifier [String, Number] This is a unique identifier for the prediction, which will associate it with a true value event and allow you to look it up in the app.
|
330
|
+
# @param true_value [String, Number] This is the true value for the prediction.
|
331
|
+
def enqueue_log_true_value(identifier:, true_value:)
|
332
|
+
event = true_value_event(
|
333
|
+
identifier: identifier,
|
334
|
+
true_value: true_value
|
335
|
+
)
|
336
|
+
log_queue.push(event)
|
337
|
+
end
|
338
|
+
|
339
|
+
# Send all events in the queue to the app.
|
340
|
+
def flush_log_queue
|
341
|
+
log_events(@log_queue)
|
342
|
+
@log_queue = []
|
343
|
+
end
|
344
|
+
|
345
|
+
private
|
346
|
+
|
347
|
+
def log_event(event)
|
348
|
+
log_events([event])
|
349
|
+
end
|
350
|
+
|
351
|
+
def log_events(events)
|
352
|
+
headers = {
|
353
|
+
'Content-Type': 'application/json'
|
354
|
+
}
|
355
|
+
uri = URI("#{@tangram_url}/track")
|
356
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
357
|
+
request = Net::HTTP::Post.new(uri.request_uri, headers)
|
358
|
+
request.body = events.to_json
|
359
|
+
response = http.request(request)
|
360
|
+
raise response unless response.is_a? Net::HTTPSuccess
|
361
|
+
end
|
362
|
+
|
363
|
+
def prediction_event(identifier:, input:, output:, options: nil)
|
364
|
+
{
|
365
|
+
date: DateTime.now.rfc3339,
|
366
|
+
identifier: identifier,
|
367
|
+
input: input,
|
368
|
+
model_id: id,
|
369
|
+
options: options,
|
370
|
+
output: output,
|
371
|
+
type: 'prediction'
|
372
|
+
}
|
373
|
+
end
|
374
|
+
|
375
|
+
def true_value_event(identifier:, true_value:)
|
376
|
+
{
|
377
|
+
date: DateTime.now.rfc3339,
|
378
|
+
identifier: identifier,
|
379
|
+
model_id: id,
|
380
|
+
true_value: true_value,
|
381
|
+
type: 'true_value'
|
382
|
+
}
|
383
|
+
end
|
384
|
+
|
385
|
+
def new_predict_options(options)
|
386
|
+
c_options = FFI::MemoryPointer.new(:pointer)
|
387
|
+
LibTangram.tangram_predict_options_new(c_options)
|
388
|
+
c_options = FFI::AutoPointer.new(c_options.read_pointer, LibTangram.method(:tangram_predict_options_delete))
|
389
|
+
unless options.nil?
|
390
|
+
unless options.threshold.nil?
|
391
|
+
LibTangram.tangram_predict_options_set_threshold(c_options, options.threshold)
|
392
|
+
end
|
393
|
+
unless options.compute_feature_contributions.nil?
|
394
|
+
LibTangram.tangram_predict_options_set_compute_feature_contributions(c_options, options.compute_feature_contributions)
|
395
|
+
end
|
396
|
+
end
|
397
|
+
c_options
|
398
|
+
end
|
399
|
+
|
400
|
+
def new_predict_input_vec(input_vec)
|
401
|
+
c_inputs = FFI::MemoryPointer.new(:pointer)
|
402
|
+
LibTangram.tangram_predict_input_vec_new(c_inputs)
|
403
|
+
c_inputs = FFI::AutoPointer.new(c_inputs.read_pointer, LibTangram.method(:tangram_predict_input_vec_delete))
|
404
|
+
(0...input_vec.length).each do |input_index|
|
405
|
+
input = input_vec[input_index]
|
406
|
+
predict_input = new_predict_input(input)
|
407
|
+
LibTangram.tangram_predict_input_vec_push(c_inputs, predict_input)
|
408
|
+
end
|
409
|
+
c_inputs
|
410
|
+
end
|
411
|
+
|
412
|
+
def new_predict_input(input)
|
413
|
+
c_input = FFI::MemoryPointer.new(:pointer)
|
414
|
+
LibTangram.tangram_predict_input_new(c_input)
|
415
|
+
c_input = c_input.read_pointer
|
416
|
+
input.each do |key, value|
|
417
|
+
is_float = value.is_a?(Float)
|
418
|
+
is_string = value.is_a?(String)
|
419
|
+
if is_float
|
420
|
+
LibTangram.tangram_predict_input_set_value_number(c_input, key.to_s, value)
|
421
|
+
elsif is_string
|
422
|
+
LibTangram.tangram_predict_input_set_value_string(c_input, key.to_s, value)
|
423
|
+
else
|
424
|
+
raise 'tangram error'
|
425
|
+
end
|
426
|
+
end
|
427
|
+
c_input
|
428
|
+
end
|
429
|
+
|
430
|
+
def predict_output_vec_from_tangram_predict_output_vec(c_output_vec)
|
431
|
+
outputs = []
|
432
|
+
c_output_len = FFI::MemoryPointer.new(:int)
|
433
|
+
LibTangram.tangram_predict_output_vec_len(c_output_vec, c_output_len)
|
434
|
+
output_len = c_output_len.read(:int)
|
435
|
+
(0...output_len).each do |output_index|
|
436
|
+
c_output = FFI::MemoryPointer.new(:pointer)
|
437
|
+
LibTangram.tangram_predict_output_vec_get_at_index(c_output_vec, output_index, c_output)
|
438
|
+
c_output = c_output.read_pointer
|
439
|
+
outputs.push(predict_output_from_tangram_predict_output(c_output))
|
440
|
+
end
|
441
|
+
|
442
|
+
outputs
|
443
|
+
end
|
444
|
+
|
445
|
+
def predict_output_from_tangram_predict_output(c_output)
|
446
|
+
c_task_type = FFI::MemoryPointer.new(:int)
|
447
|
+
LibTangram.tangram_model_get_task(@model, c_task_type)
|
448
|
+
case c_task_type.read(:int)
|
449
|
+
when LibTangram::TangramTaskType[:regression]
|
450
|
+
regression_output_from_tangram_predict_output(c_output)
|
451
|
+
when LibTangram::TangramTaskType[:binary_classification]
|
452
|
+
binary_classification_output_from_tangram_predict_output(c_output)
|
453
|
+
when LibTangram::TangramTaskType[:multiclass_classification]
|
454
|
+
multiclass_classification_output_from_tangram_predict_output(c_output)
|
455
|
+
end
|
456
|
+
end
|
457
|
+
|
458
|
+
def regression_output_from_tangram_predict_output(c_output)
|
459
|
+
c_regression_output = FFI::MemoryPointer.new(:pointer)
|
460
|
+
LibTangram.tangram_predict_output_as_regression(c_output, c_regression_output)
|
461
|
+
c_regression_output = c_regression_output.read_pointer
|
462
|
+
c_value = FFI::MemoryPointer.new(:float)
|
463
|
+
LibTangram.tangram_regression_predict_output_get_value(c_regression_output, c_value)
|
464
|
+
value = c_value.read(:float)
|
465
|
+
c_feature_contributions = FFI::MemoryPointer.new(:pointer)
|
466
|
+
LibTangram.tangram_regression_predict_output_get_feature_contributions(c_regression_output, c_feature_contributions)
|
467
|
+
c_feature_contributions = c_feature_contributions.read_pointer
|
468
|
+
feature_contributions = c_feature_contributions.null? ? {} : get_feature_contributions(c_feature_contributions)
|
469
|
+
RegressionPredictOutput.new(
|
470
|
+
value: value,
|
471
|
+
feature_contributions: feature_contributions
|
472
|
+
)
|
473
|
+
end
|
474
|
+
|
475
|
+
def binary_classification_output_from_tangram_predict_output(c_output)
|
476
|
+
c_binary_classification_output = FFI::MemoryPointer.new(:pointer)
|
477
|
+
LibTangram.tangram_predict_output_as_binary_classification(c_output, c_binary_classification_output)
|
478
|
+
c_binary_classification_output = c_binary_classification_output.read_pointer
|
479
|
+
c_probability = FFI::MemoryPointer.new(:float)
|
480
|
+
LibTangram.tangram_binary_classification_predict_output_get_probability(c_binary_classification_output, c_probability)
|
481
|
+
probability = c_probability.read(:float)
|
482
|
+
c_class_name = LibTangram::TangramStringView.new
|
483
|
+
LibTangram.tangram_binary_classification_predict_output_get_class_name(c_binary_classification_output, c_class_name)
|
484
|
+
class_name = c_class_name.into_string
|
485
|
+
c_feature_contributions = FFI::MemoryPointer.new(:pointer)
|
486
|
+
LibTangram.tangram_binary_classification_predict_output_get_feature_contributions(c_binary_classification_output, c_feature_contributions)
|
487
|
+
c_feature_contributions = c_feature_contributions.read_pointer
|
488
|
+
feature_contributions = c_feature_contributions.null? ? {} : get_feature_contributions(c_feature_contributions)
|
489
|
+
BinaryClassificationPredictOutput.new(
|
490
|
+
class_name: class_name,
|
491
|
+
probability: probability,
|
492
|
+
feature_contributions: feature_contributions
|
493
|
+
)
|
494
|
+
end
|
495
|
+
|
496
|
+
def multiclass_classification_output_from_tangram_predict_output(c_output)
|
497
|
+
c_multiclass_classification_output = FFI::MemoryPointer.new(:pointer)
|
498
|
+
LibTangram.tangram_predict_output_as_multiclass_classification(c_output, c_multiclass_classification_output)
|
499
|
+
c_multiclass_classification_output = c_multiclass_classification_output.read_pointer
|
500
|
+
c_probability = FFI::MemoryPointer.new(:float)
|
501
|
+
LibTangram.tangram_multiclass_classification_predict_output_get_probability(c_multiclass_classification_output, c_probability)
|
502
|
+
probability = c_probability.read(:float)
|
503
|
+
c_class_name = LibTangram::TangramStringView.new
|
504
|
+
LibTangram.tangram_multiclass_classification_predict_output_get_class_name(c_multiclass_classification_output, c_class_name)
|
505
|
+
class_name = c_class_name.into_string
|
506
|
+
probabilities = multiclass_classification_output_get_probabilities(c_multiclass_classification_output)
|
507
|
+
feature_contributions = multiclass_classification_output_get_feature_contributions(c_multiclass_classification_output)
|
508
|
+
MulticlassClassificationPredictOutput.new(
|
509
|
+
class_name: class_name,
|
510
|
+
probability: probability,
|
511
|
+
probabilities: probabilities,
|
512
|
+
feature_contributions: feature_contributions
|
513
|
+
)
|
514
|
+
end
|
515
|
+
|
516
|
+
def multiclass_classification_output_get_probabilities(c_multiclass_classification_output)
|
517
|
+
probabilities = {}
|
518
|
+
c_probabilities_iter = FFI::MemoryPointer.new(:pointer)
|
519
|
+
LibTangram.tangram_multiclass_classification_predict_output_get_probabilities_iter(c_multiclass_classification_output, c_probabilities_iter)
|
520
|
+
c_probabilities_iter = FFI::AutoPointer.new(c_probabilities_iter.read_pointer, LibTangram.method(:tangram_multiclass_classification_predict_output_probabilities_iter_delete))
|
521
|
+
c_class_probability = FFI::MemoryPointer.new(:float)
|
522
|
+
c_class_name = LibTangram::TangramStringView.new
|
523
|
+
while LibTangram.tangram_multiclass_classification_predict_output_probabilities_iter_next(c_probabilities_iter, c_class_name, c_class_probability)
|
524
|
+
class_name = c_class_name.into_string
|
525
|
+
probabilities[class_name] = c_class_probability.read(:float)
|
526
|
+
end
|
527
|
+
probabilities
|
528
|
+
end
|
529
|
+
|
530
|
+
def multiclass_classification_output_get_feature_contributions(c_multiclass_classification_output)
|
531
|
+
c_feature_contributions_iter = FFI::MemoryPointer.new(:pointer)
|
532
|
+
LibTangram.tangram_multiclass_classification_predict_output_get_feature_contributions_iter(c_multiclass_classification_output, c_feature_contributions_iter)
|
533
|
+
c_feature_contributions_iter = FFI::AutoPointer.new(c_feature_contributions_iter.read_pointer, LibTangram.method(:tangram_multiclass_classification_predict_output_feature_contributions_iter_delete))
|
534
|
+
feature_contributions = {}
|
535
|
+
unless c_feature_contributions_iter.null?
|
536
|
+
c_class_name = LibTangram::TangramStringView.new
|
537
|
+
c_feature_contributions_ptr = FFI::MemoryPointer.new(:pointer)
|
538
|
+
while LibTangram.tangram_multiclass_classification_predict_output_feature_contributions_iter_next(c_feature_contributions_iter, c_class_name, c_feature_contributions_ptr)
|
539
|
+
class_name = c_class_name.into_string
|
540
|
+
c_feature_contributions = c_feature_contributions_ptr.read_pointer
|
541
|
+
unless c_feature_contributions.null?
|
542
|
+
feature_contributions[class_name] = get_feature_contributions(c_feature_contributions)
|
543
|
+
end
|
544
|
+
end
|
545
|
+
end
|
546
|
+
feature_contributions
|
547
|
+
end
|
548
|
+
|
549
|
+
def get_feature_contributions(c_feature_contributions)
|
550
|
+
c_baseline = FFI::MemoryPointer.new(:float)
|
551
|
+
LibTangram.tangram_feature_contributions_get_baseline_value(c_feature_contributions, c_baseline)
|
552
|
+
baseline = c_baseline.read(:float)
|
553
|
+
c_output = FFI::MemoryPointer.new(:float)
|
554
|
+
LibTangram.tangram_feature_contributions_get_output_value(c_feature_contributions, c_output)
|
555
|
+
output = c_output.read(:float)
|
556
|
+
feature_contribution_entries = get_feature_contributions_entries(c_feature_contributions)
|
557
|
+
FeatureContributions.new(
|
558
|
+
baseline: baseline,
|
559
|
+
output: output,
|
560
|
+
entries: feature_contribution_entries
|
561
|
+
)
|
562
|
+
end
|
563
|
+
|
564
|
+
def get_feature_contributions_entries(c_feature_contributions)
|
565
|
+
c_len = FFI::MemoryPointer.new(:int)
|
566
|
+
LibTangram.tangram_feature_contributions_get_entries_len(c_feature_contributions, c_len)
|
567
|
+
len = c_len.read(:int)
|
568
|
+
feature_contributions = []
|
569
|
+
(0...len).each do |i|
|
570
|
+
c_feature_contribution = FFI::MemoryPointer.new(:pointer)
|
571
|
+
LibTangram.tangram_feature_contributions_get_entry_at_index(c_feature_contributions, i, c_feature_contribution)
|
572
|
+
c_feature_contribution = c_feature_contribution.read_pointer
|
573
|
+
feature_contributions.push(get_feature_contribution(c_feature_contribution))
|
574
|
+
end
|
575
|
+
feature_contributions
|
576
|
+
end
|
577
|
+
|
578
|
+
def get_feature_contribution(c_feature_contribution)
|
579
|
+
c_feature_contribution_type = FFI::MemoryPointer.new(:int)
|
580
|
+
LibTangram.tangram_feature_contribution_entry_get_type(c_feature_contribution, c_feature_contribution_type)
|
581
|
+
case c_feature_contribution_type.read(:int)
|
582
|
+
when LibTangram::TangramFeatureContributionEntryType[:identity]
|
583
|
+
get_identity_feature_contribution(c_feature_contribution)
|
584
|
+
when LibTangram::TangramFeatureContributionEntryType[:normalized]
|
585
|
+
get_normalized_feature_contribution(c_feature_contribution)
|
586
|
+
when LibTangram::TangramFeatureContributionEntryType[:one_hot_encoded]
|
587
|
+
get_one_hot_encoded_feature_contribution(c_feature_contribution)
|
588
|
+
when LibTangram::TangramFeatureContributionEntryType[:bag_of_words]
|
589
|
+
get_bag_of_words_feature_contribution(c_feature_contribution)
|
590
|
+
when LibTangram::TangramFeatureContributionEntryType[:bag_of_words_cosine_similarity]
|
591
|
+
get_bag_of_words_cosine_similarity_feature_contribution(c_feature_contribution)
|
592
|
+
when LibTangram::TangramFeatureContributionEntryType[:word_embedding]
|
593
|
+
get_word_embedding_feature_contribution(c_feature_contribution)
|
594
|
+
end
|
595
|
+
end
|
596
|
+
|
597
|
+
def get_identity_feature_contribution(c_feature_contribution)
|
598
|
+
c_identity_feature_contribution = FFI::MemoryPointer.new(:pointer)
|
599
|
+
LibTangram.tangram_feature_contribution_entry_as_identity(c_feature_contribution, c_identity_feature_contribution)
|
600
|
+
c_identity_feature_contribution = c_identity_feature_contribution.read_pointer
|
601
|
+
c_column_name = LibTangram::TangramStringView.new
|
602
|
+
LibTangram.tangram_identity_feature_contribution_get_column_name(c_identity_feature_contribution, c_column_name)
|
603
|
+
column_name = c_column_name.into_string
|
604
|
+
c_feature_contribution_value = FFI::MemoryPointer.new(:float)
|
605
|
+
LibTangram.tangram_identity_feature_contribution_get_feature_contribution_value(c_identity_feature_contribution, c_feature_contribution_value)
|
606
|
+
feature_contribution_value = c_feature_contribution_value.read(:float)
|
607
|
+
c_feature_value = FFI::MemoryPointer.new(:float)
|
608
|
+
LibTangram.tangram_identity_feature_contribution_get_feature_value(c_identity_feature_contribution, c_feature_value)
|
609
|
+
feature_value = c_feature_value.read(:float)
|
610
|
+
IdentityFeatureContribution.new(
|
611
|
+
column_name: column_name,
|
612
|
+
feature_value: feature_value,
|
613
|
+
feature_contribution_value: feature_contribution_value
|
614
|
+
)
|
615
|
+
end
|
616
|
+
|
617
|
+
def get_normalized_feature_contribution(c_feature_contribution)
|
618
|
+
c_normalized_feature_contribution = FFI::MemoryPointer.new(:pointer)
|
619
|
+
LibTangram.tangram_feature_contribution_entry_as_normalized(
|
620
|
+
c_feature_contribution, c_normalized_feature_contribution
|
621
|
+
)
|
622
|
+
c_normalized_feature_contribution = c_normalized_feature_contribution.read_pointer
|
623
|
+
c_column_name = LibTangram::TangramStringView.new
|
624
|
+
LibTangram.tangram_normalized_feature_contribution_get_column_name(c_normalized_feature_contribution, c_column_name)
|
625
|
+
column_name = c_column_name.into_string
|
626
|
+
c_feature_value = FFI::MemoryPointer.new(:float)
|
627
|
+
LibTangram.tangram_normalized_feature_contribution_get_feature_value(c_normalized_feature_contribution, c_feature_value)
|
628
|
+
feature_value = c_feature_value.read(:float)
|
629
|
+
c_feature_contribution_value = FFI::MemoryPointer.new(:float)
|
630
|
+
LibTangram.tangram_normalized_feature_contribution_get_feature_contribution_value(c_normalized_feature_contribution, c_feature_contribution_value)
|
631
|
+
feature_contribution_value = c_feature_contribution_value.read(:float)
|
632
|
+
NormalizedFeatureContribution.new(
|
633
|
+
column_name: column_name,
|
634
|
+
feature_value: feature_value,
|
635
|
+
feature_contribution_value: feature_contribution_value
|
636
|
+
)
|
637
|
+
end
|
638
|
+
|
639
|
+
def get_one_hot_encoded_feature_contribution(c_feature_contribution)
|
640
|
+
c_one_hot_encoded_feature_contribution = FFI::MemoryPointer.new(:pointer)
|
641
|
+
LibTangram.tangram_feature_contribution_entry_as_one_hot_encoded(c_feature_contribution, c_one_hot_encoded_feature_contribution)
|
642
|
+
c_one_hot_encoded_feature_contribution = c_one_hot_encoded_feature_contribution.read_pointer
|
643
|
+
c_column_name = LibTangram::TangramStringView.new
|
644
|
+
LibTangram.tangram_one_hot_encoded_feature_contribution_get_column_name(c_one_hot_encoded_feature_contribution, c_column_name)
|
645
|
+
column_name = c_column_name.into_string
|
646
|
+
c_feature_contribution_value = FFI::MemoryPointer.new(:float)
|
647
|
+
LibTangram.tangram_one_hot_encoded_feature_contribution_get_feature_contribution_value(c_one_hot_encoded_feature_contribution, c_feature_contribution_value)
|
648
|
+
feature_contribution_value = c_feature_contribution_value.read(:float)
|
649
|
+
c_feature_value = FFI::MemoryPointer.new(:bool)
|
650
|
+
LibTangram.tangram_one_hot_encoded_feature_contribution_get_feature_value(c_one_hot_encoded_feature_contribution, c_feature_value)
|
651
|
+
feature_value = c_feature_value.read(:bool)
|
652
|
+
c_variant = LibTangram::TangramStringView.new
|
653
|
+
LibTangram.tangram_one_hot_encoded_feature_contribution_get_variant(c_one_hot_encoded_feature_contribution, c_variant)
|
654
|
+
variant = c_variant[:ptr].null? ? nil : c_variant.into_string
|
655
|
+
OneHotEncodedFeatureContribution.new(
|
656
|
+
column_name: column_name,
|
657
|
+
variant: variant,
|
658
|
+
feature_contribution_value: feature_contribution_value,
|
659
|
+
feature_value: feature_value
|
660
|
+
)
|
661
|
+
end
|
662
|
+
|
663
|
+
def get_bag_of_words_feature_contribution(c_feature_contribution)
|
664
|
+
c_bag_of_words_feature_contribution = FFI::MemoryPointer.new(:pointer)
|
665
|
+
LibTangram.tangram_feature_contribution_entry_as_bag_of_words(c_feature_contribution, c_bag_of_words_feature_contribution)
|
666
|
+
c_bag_of_words_feature_contribution = c_bag_of_words_feature_contribution.read_pointer
|
667
|
+
c_column_name = LibTangram::TangramStringView.new
|
668
|
+
LibTangram.tangram_bag_of_words_feature_contribution_get_column_name(c_bag_of_words_feature_contribution, c_column_name)
|
669
|
+
column_name = c_column_name.into_string
|
670
|
+
c_feature_contribution_value = FFI::MemoryPointer.new(:float)
|
671
|
+
LibTangram.tangram_bag_of_words_feature_contribution_get_feature_contribution_value(c_bag_of_words_feature_contribution, c_feature_contribution_value)
|
672
|
+
feature_contribution_value = c_feature_contribution_value.read(:float)
|
673
|
+
c_feature_value = FFI::MemoryPointer.new(:float)
|
674
|
+
LibTangram.tangram_bag_of_words_feature_contribution_get_feature_value(c_bag_of_words_feature_contribution, c_feature_value)
|
675
|
+
feature_value = c_feature_value.read(:float)
|
676
|
+
c_ngram = FFI::MemoryPointer(:pointer)
|
677
|
+
LibTangram.tangram_bag_of_words_feature_contribution_get_ngram(c_bag_of_words_feature_contribution, c_ngram)
|
678
|
+
c_ngram = c_ngram.read_pointer
|
679
|
+
ngram = get_ngram(c_ngram)
|
680
|
+
BagOfWordsFeatureContribution.new(
|
681
|
+
column_name: column_name,
|
682
|
+
ngram: ngram,
|
683
|
+
feature_contribution_value: feature_contribution_value,
|
684
|
+
feature_value: feature_value
|
685
|
+
)
|
686
|
+
end
|
687
|
+
|
688
|
+
def get_bag_of_words_cosine_similarity_feature_contribution(c_feature_contribution)
|
689
|
+
c_bag_of_words_cosine_similarity_feature_contribution = FFI::MemoryPointer.new(:pointer)
|
690
|
+
LibTangram.tangram_feature_contribution_entry_as_bag_of_words_cosine_similarity(c_feature_contribution, c_bag_of_words_cosine_similarity_feature_contribution)
|
691
|
+
c_bag_of_words_cosine_similarity_feature_contribution = c_bag_of_words_cosine_similarity_feature_contribution.read_pointer
|
692
|
+
c_column_name_a = LibTangram::TangramStringView.new
|
693
|
+
LibTangram.tangram_bag_of_words_feature_contribution_get_column_name_a(c_bag_of_words_cosine_similarity_feature_contribution, c_column_name_a)
|
694
|
+
column_name_a = c_column_name_a.into_string
|
695
|
+
c_column_name_b = LibTangram::TangramStringView.new
|
696
|
+
LibTangram.tangram_bag_of_words_feature_contribution_get_column_name_b(c_bag_of_words_cosine_similarity_feature_contribution, c_column_name_b)
|
697
|
+
column_name_b = c_column_name_b.into_string
|
698
|
+
c_feature_contribution_value = FFI::MemoryPointer.new(:float)
|
699
|
+
LibTangram.tangram_bag_of_words_cosine_similarity_feature_contribution_get_feature_contribution_value(c_bag_of_words_cosine_similarity_feature_contribution, c_feature_contribution_value)
|
700
|
+
feature_contribution_value = c_feature_contribution_value.read(:float)
|
701
|
+
c_feature_value = FFI::MemoryPointer.new(:float)
|
702
|
+
LibTangram.tangram_bag_of_words_cosine_similarity_feature_contribution_get_feature_value(c_bag_of_words_cosine_similarity_feature_contribution, c_feature_value)
|
703
|
+
feature_value = c_feature_value.read(:float)
|
704
|
+
BagOfWordsFeatureContribution.new(
|
705
|
+
column_name_a: column_name_a,
|
706
|
+
column_name_b: column_name_b,
|
707
|
+
feature_contribution_value: feature_contribution_value,
|
708
|
+
feature_value: feature_value
|
709
|
+
)
|
710
|
+
end
|
711
|
+
|
712
|
+
def get_word_embedding_feature_contribution(c_feature_contribution)
|
713
|
+
c_word_embedding_feature_contribution = FFI::MemoryPointer.new(:pointer)
|
714
|
+
LibTangram.tangram_feature_contribution_entry_as_word_embedding(
|
715
|
+
c_feature_contribution, c_word_embedding_feature_contribution
|
716
|
+
)
|
717
|
+
c_word_embedding_feature_contribution = c_word_embedding_feature_contribution.read_pointer
|
718
|
+
c_column_name = LibTangram::TangramStringView.new
|
719
|
+
LibTangram.tangram_word_embedding_feature_contribution_get_column_name(c_word_embedding_feature_contribution, c_column_name)
|
720
|
+
column_name = c_column_name.into_string
|
721
|
+
c_feature_contribution_value = FFI::MemoryPointer.new(:float)
|
722
|
+
LibTangram.tangram_word_embedding_feature_contribution_get_feature_contribution_value(c_word_embedding_feature_contribution, c_feature_contribution_value)
|
723
|
+
feature_contribution_value = c_feature_contribution_value.read(:float)
|
724
|
+
c_value_index = FFI::MemoryPointer.new(:int)
|
725
|
+
LibTangram.tangram_word_embedding_feature_contribution_get_value_index(c_word_embedding_feature_contribution, c_value_index)
|
726
|
+
value_index = c_value_index.read(:int)
|
727
|
+
WordEmbeddingFeatureContribution.new(
|
728
|
+
column_name: column_name,
|
729
|
+
value_index: value_index,
|
730
|
+
feature_contribution_value: feature_contribution_value
|
731
|
+
)
|
732
|
+
end
|
733
|
+
|
734
|
+
def get_ngram(ngram)
|
735
|
+
c_ngram_type = FFI::MemoryPointer.new(:int)
|
736
|
+
LibTangram.tangram_ngram_get_type(ngram, ngram_type)
|
737
|
+
case c_ngram_type.read(:int)
|
738
|
+
when LibTangram::TangramNGramType[:unigram]
|
739
|
+
get_unigram_ngram(ngram)
|
740
|
+
when LibTangram::TangramNGramType[:bigram]
|
741
|
+
get_bigram_ngram(ngram)
|
742
|
+
end
|
743
|
+
end
|
744
|
+
|
745
|
+
def get_unigram_ngram(ngram)
|
746
|
+
c_token = LibTangram::TangramStringView.new
|
747
|
+
LibTangram.tangram_unigram_get_token(ngram, c_token)
|
748
|
+
token = c_token.into_string
|
749
|
+
Unigram.new(
|
750
|
+
token: token
|
751
|
+
)
|
752
|
+
end
|
753
|
+
|
754
|
+
def get_bigram_ngram(ngram)
|
755
|
+
c_token_a = LibTangram::TangramStringView.new
|
756
|
+
c_token_b = LibTangram::TangramStringView.new
|
757
|
+
LibTangram.tangram_bigram_get_token_a(ngram, c_token_a)
|
758
|
+
LibTangram.tangram_bigram_get_token_b(ngram, c_token_b)
|
759
|
+
token_a = c_token_a.into_string
|
760
|
+
token_b = c_token_b.into_string
|
761
|
+
Bigram.new(
|
762
|
+
token_a: token_a,
|
763
|
+
token_b: token_b
|
764
|
+
)
|
765
|
+
end
|
766
|
+
end
|
767
|
+
|
768
|
+
module LibTangram
|
769
|
+
cpu = RbConfig::CONFIG['host_cpu']
|
770
|
+
os = RbConfig::CONFIG['host_os']
|
771
|
+
if (cpu == 'x86_64') && os =~ (/linux/)
|
772
|
+
library_path = 'libtangram/x86_64-unknown-linux-gnu/libtangram.so'
|
773
|
+
elsif (cpu == 'aarch64') && os =~ (/linux/)
|
774
|
+
library_path = 'libtangram/aarch64-unknown-linux-gnu/libtangram.so'
|
775
|
+
elsif (cpu == 'x86_64') && os =~ (/darwin/)
|
776
|
+
library_path = 'libtangram/x86_64-apple-darwin/libtangram.dylib'
|
777
|
+
elsif (cpu == 'aarch64') && os =~ (/darwin/)
|
778
|
+
library_path = 'libtangram/aarch64-apple-darwin/libtangram.dylib'
|
779
|
+
elsif (cpu == 'x86_64') && os =~ (/mingw/)
|
780
|
+
library_path = 'libtangram/x86_64-pc-windows-msvc/tangram.dll'
|
781
|
+
else
|
782
|
+
raise 'Tangram for Ruby does not yet support your combination of CPU architecture and operating system. Open an issue at https://github.com/tangramxyz/tangram/issues/new or email us at help@tangram.xyz to complain.'
|
783
|
+
end
|
784
|
+
extend FFI::Library
|
785
|
+
ffi_lib File.expand_path(library_path.to_s, __dir__)
|
786
|
+
|
787
|
+
class TangramStringView < FFI::Struct
|
788
|
+
layout :ptr, :pointer,
|
789
|
+
:len, :int
|
790
|
+
def into_string
|
791
|
+
self[:ptr].read_string(self[:len]).force_encoding('utf-8')
|
792
|
+
end
|
793
|
+
end
|
794
|
+
|
795
|
+
TangramTaskType = enum(
|
796
|
+
:regression,
|
797
|
+
:binary_classification,
|
798
|
+
:multiclass_classification
|
799
|
+
)
|
800
|
+
|
801
|
+
TangramFeatureContributionEntryType = enum(
|
802
|
+
:identity,
|
803
|
+
:normalized,
|
804
|
+
:one_hot_encoded,
|
805
|
+
:bag_of_words,
|
806
|
+
:bag_of_words_cosine_similarity,
|
807
|
+
:word_embedding
|
808
|
+
)
|
809
|
+
|
810
|
+
TangramNGramType = enum(
|
811
|
+
:unigram,
|
812
|
+
:bigram
|
813
|
+
)
|
814
|
+
|
815
|
+
typedef :pointer, :tangram_error
|
816
|
+
|
817
|
+
attach_function :tangram_error_get_message, [:pointer, TangramStringView.by_ref], :void
|
818
|
+
attach_function :tangram_error_delete, [:pointer], :void
|
819
|
+
attach_function :tangram_version, [TangramStringView.by_ref], :void
|
820
|
+
attach_function :tangram_model_from_bytes, [:pointer, :int, :pointer], :tangram_error
|
821
|
+
attach_function :tangram_model_from_path, [:string, :pointer], :tangram_error
|
822
|
+
attach_function :tangram_model_delete, [:pointer], :void
|
823
|
+
attach_function :tangram_model_get_id, [:pointer, TangramStringView.by_ref], :void
|
824
|
+
attach_function :tangram_model_get_task, [:pointer, :pointer], :void
|
825
|
+
attach_function :tangram_predict_input_new, [:pointer], :void
|
826
|
+
attach_function :tangram_predict_input_delete, [:pointer], :void
|
827
|
+
attach_function :tangram_predict_input_set_value_number, [:pointer, :string, :double], :int
|
828
|
+
attach_function :tangram_predict_input_set_value_string, [:pointer, :string, :string], :int
|
829
|
+
attach_function :tangram_predict_input_vec_new, [:pointer], :void
|
830
|
+
attach_function :tangram_predict_input_vec_delete, [:pointer], :void
|
831
|
+
attach_function :tangram_predict_input_vec_push, [:pointer, :pointer], :void
|
832
|
+
attach_function :tangram_predict_options_new, [:pointer], :void
|
833
|
+
attach_function :tangram_predict_options_delete, [:pointer], :void
|
834
|
+
attach_function :tangram_predict_options_set_threshold, [:pointer, :float], :void
|
835
|
+
attach_function :tangram_predict_options_set_compute_feature_contributions, [:pointer, :bool], :void
|
836
|
+
attach_function :tangram_model_predict, [:pointer, :pointer, :pointer, :pointer], :tangram_error
|
837
|
+
attach_function :tangram_predict_output_delete, [:pointer], :void
|
838
|
+
attach_function :tangram_predict_output_vec_delete, [:pointer], :void
|
839
|
+
attach_function :tangram_predict_output_vec_len, [:pointer, :pointer], :void
|
840
|
+
attach_function :tangram_predict_output_vec_get_at_index, [:pointer, :int, :pointer], :void
|
841
|
+
attach_function :tangram_predict_output_as_regression, [:pointer, :pointer], :void
|
842
|
+
attach_function :tangram_predict_output_as_binary_classification, [:pointer, :pointer], :void
|
843
|
+
attach_function :tangram_predict_output_as_multiclass_classification, [:pointer, :pointer], :void
|
844
|
+
attach_function :tangram_regression_predict_output_get_value, [:pointer, :pointer], :void
|
845
|
+
attach_function :tangram_regression_predict_output_get_feature_contributions, [:pointer, :pointer], :void
|
846
|
+
attach_function :tangram_binary_classification_predict_output_get_class_name, [:pointer, TangramStringView.by_ref], :void
|
847
|
+
attach_function :tangram_binary_classification_predict_output_get_probability, [:pointer, :pointer], :void
|
848
|
+
attach_function :tangram_binary_classification_predict_output_get_feature_contributions, [:pointer, :pointer], :void
|
849
|
+
attach_function :tangram_multiclass_classification_predict_output_get_class_name, [:pointer, TangramStringView.by_ref], :void
|
850
|
+
attach_function :tangram_multiclass_classification_predict_output_get_probability, [:pointer, :pointer], :void
|
851
|
+
attach_function :tangram_multiclass_classification_predict_output_get_probabilities_len, [:pointer, :pointer], :void
|
852
|
+
attach_function :tangram_multiclass_classification_predict_output_probabilities_iter_delete, [:pointer], :void
|
853
|
+
attach_function :tangram_multiclass_classification_predict_output_get_probabilities_iter, [:pointer, :pointer], :void
|
854
|
+
attach_function :tangram_multiclass_classification_predict_output_probabilities_iter_next, [:pointer, :pointer, :pointer], :bool
|
855
|
+
attach_function :tangram_multiclass_classification_predict_output_feature_contributions_iter_delete, [:pointer], :void
|
856
|
+
attach_function :tangram_multiclass_classification_predict_output_get_feature_contributions_iter, [:pointer, :pointer], :void
|
857
|
+
attach_function :tangram_multiclass_classification_predict_output_feature_contributions_iter_next, [:pointer, :pointer, :pointer], :bool
|
858
|
+
attach_function :tangram_feature_contributions_get_baseline_value, [:pointer, :pointer], :void
|
859
|
+
attach_function :tangram_feature_contributions_get_output_value, [:pointer, :pointer], :void
|
860
|
+
attach_function :tangram_feature_contributions_get_entries_len, [:pointer, :pointer], :void
|
861
|
+
attach_function :tangram_feature_contributions_get_entry_at_index, [:pointer, :int, :pointer], :void
|
862
|
+
attach_function :tangram_feature_contribution_entry_get_type, [:pointer, :pointer], :void
|
863
|
+
attach_function :tangram_feature_contribution_entry_as_identity, [:pointer, :pointer], :void
|
864
|
+
attach_function :tangram_feature_contribution_entry_as_normalized, [:pointer, :pointer], :void
|
865
|
+
attach_function :tangram_feature_contribution_entry_as_one_hot_encoded, [:pointer, :pointer], :void
|
866
|
+
attach_function :tangram_feature_contribution_entry_as_bag_of_words, [:pointer, :pointer], :void
|
867
|
+
attach_function :tangram_feature_contribution_entry_as_bag_of_words_cosine_similarity, [:pointer, :pointer], :void
|
868
|
+
attach_function :tangram_feature_contribution_entry_as_word_embedding, [:pointer, :pointer], :void
|
869
|
+
attach_function :tangram_identity_feature_contribution_get_column_name, [:pointer, TangramStringView.by_ref], :void
|
870
|
+
attach_function :tangram_identity_feature_contribution_get_feature_value, [:pointer, :pointer], :void
|
871
|
+
attach_function :tangram_identity_feature_contribution_get_feature_contribution_value, [:pointer, :pointer], :void
|
872
|
+
attach_function :tangram_normalized_feature_contribution_get_column_name, [:pointer, TangramStringView.by_ref], :void
|
873
|
+
attach_function :tangram_normalized_feature_contribution_get_feature_value, [:pointer, :pointer], :void
|
874
|
+
attach_function :tangram_normalized_feature_contribution_get_feature_contribution_value, [:pointer, :pointer], :void
|
875
|
+
attach_function :tangram_one_hot_encoded_feature_contribution_get_column_name, [:pointer, TangramStringView.by_ref], :void
|
876
|
+
attach_function :tangram_one_hot_encoded_feature_contribution_get_variant, [:pointer, TangramStringView.by_ref], :void
|
877
|
+
attach_function :tangram_one_hot_encoded_feature_contribution_get_feature_value, [:pointer, :pointer], :void
|
878
|
+
attach_function :tangram_one_hot_encoded_feature_contribution_get_feature_contribution_value, [:pointer, :pointer], :void
|
879
|
+
attach_function :tangram_bag_of_words_feature_contribution_get_column_name, [:pointer, TangramStringView.by_ref], :void
|
880
|
+
attach_function :tangram_bag_of_words_feature_contribution_get_feature_value, [:pointer, :pointer], :void
|
881
|
+
attach_function :tangram_bag_of_words_feature_contribution_get_feature_contribution_value, [:pointer, :pointer], :void
|
882
|
+
attach_function :tangram_bag_of_words_feature_contribution_get_ngram, [:pointer, :pointer], :void
|
883
|
+
attach_function :tangram_ngram_get_type, [:pointer, :pointer], :void
|
884
|
+
attach_function :tangram_unigram_get_token, [:pointer, TangramStringView.by_ref], :void
|
885
|
+
attach_function :tangram_bigram_get_token_a, [:pointer, TangramStringView.by_ref], :void
|
886
|
+
attach_function :tangram_bigram_get_token_b, [:pointer, TangramStringView.by_ref], :void
|
887
|
+
attach_function :tangram_bag_of_words_cosine_similarity_feature_contribution_get_column_name_a, [:pointer, TangramStringView.by_ref], :void
|
888
|
+
attach_function :tangram_bag_of_words_cosine_similarity_feature_contribution_get_column_name_b, [:pointer, TangramStringView.by_ref], :void
|
889
|
+
attach_function :tangram_bag_of_words_cosine_similarity_feature_contribution_get_feature_value, [:pointer, :pointer], :void
|
890
|
+
attach_function :tangram_bag_of_words_cosine_similarity_feature_contribution_get_feature_contribution_value, [:pointer, :pointer], :void
|
891
|
+
attach_function :tangram_word_embedding_feature_contribution_get_column_name, [:pointer, TangramStringView.by_ref], :void
|
892
|
+
attach_function :tangram_word_embedding_feature_contribution_get_value_index, [:pointer, :pointer], :void
|
893
|
+
attach_function :tangram_word_embedding_feature_contribution_get_feature_contribution_value, [:pointer, :pointer], :void
|
894
|
+
end
|
895
|
+
end
|