if !Code.ensure_loaded?(Kino.SmartCell) do
defmodule Evision.SmartCell.ML.SVM do
end
else
defmodule Evision.SmartCell.ML.SVM do
use Kino.JS, assets_path: "lib/assets"
use Kino.JS.Live
use Kino.SmartCell, name: "Evision: Support Vector Machine"
alias Evision.SmartCell.Helper, as: ESCH
alias Evision.SmartCell.ML.TrainData
@smartcell_id "evision.ml.svm"
@properties %{
"data_from" => %{
:type => :string,
:opts => [must_in: ["traindata_var", "traindata"]],
:default => "traindata_var"
},
"traindata_var" => %{
:type => :string,
:default => "dataset"
},
# SVM
"type" => %{
:type => :string,
:opts => [must_in: ["C_SVC", "NU_SVC", "ONE_CLASS", "EPS_SVR", "NU_SVR"]],
:default => "C_SVC"
},
"kernel_type" => %{
:type => :string,
:opts => [must_in: ["LINEAR", "POLY", "RBF", "SIGMOID", "CHI2", "INTER", "CUSTOM"]],
:default => "RBF"
},
"to_variable" => %{
:type => :string,
:default => "svm"
},
## -- kernel parameter --
## Only used for SVM if its kernel type is one of
## [SVM::POLY, SVM::RBF, SVM::SIGMOID, SVM::CHI2]
"gamma" => %{
:type => :number,
:default => 1
},
## Only used for SVM if its kernel type is one of
## [SVM::POLY, SVM::SIGMOID]
"coef0" => %{
:type => :number,
:default => 0
},
## Only used for SVM if its kernel type is one of
## [SVM::POLY]
"degree" => %{
:type => :number,
:default => 0
},
## -- svm parameter --
## Only used for SVM if its type is one of
## [SVM::C_SVC, SVM::EPS_SVR, SVM::NU_SVR]
"c" => %{
:type => :number,
:default => 1
},
## Only used for SVM if its type is one of
## [SVM::NU_SVC, SVM::ONE_CLASS or SVM::NU_SVR]
"nu" => %{
:type => :number,
:default => 0
},
## Only used for SVM if its type is one of
## [SVM::EPS_SVR]
"p" => %{
:type => :number,
:default => 0
},
# TermCriteria
"term_criteria_type" => %{
:type => :string,
:opts => [must_in: ["max_count", "eps", "max_count+eps"]],
:default => "max_count"
},
"term_criteria_count" => %{
:type => :integer,
:opts => [minimum: 0],
:default => 10
},
"term_criteria_eps" => %{
:type => :number,
:default => 0
},
}
@inner_to_module %{
"traindata" => TrainData
}
@spec id :: String.t()
def id, do: @smartcell_id
@spec properties :: map()
def properties, do: @properties
@spec defaults :: map()
def defaults do
Map.new(Enum.map(@properties, fn {field, field_specs} ->
{field, field_specs[:default]}
end))
end
@impl true
def init(attrs, ctx) do
# load from file or fill empty entries with default values
fields =
Map.new(Enum.map(@properties, fn {field, field_specs} ->
{field, attrs[field] || field_specs[:default]}
end))
# traindata
key = "traindata"
fields = ESCH.update_key_with_module(fields, key, @inner_to_module[key], fn fields, key ->
fields["data_from"] == key
end)
info = [id: @smartcell_id, fields: fields]
{:ok, assign(ctx, info)}
end
@impl true
def handle_connect(ctx) do
{:ok, %{id: ctx.assigns.id, fields: ctx.assigns.fields}, ctx}
end
@impl true
def handle_event("update_field", %{"field" => field, "value" => value}, ctx) do
updated_fields =
case String.split(field, ".", parts: 2) do
[inner, forward] ->
ESCH.to_inner_updates(inner, @inner_to_module[inner], forward, value, ctx)
[field] ->
to_updates(ctx.assigns.fields, field, value)
end
ctx = update(ctx, :fields, &Map.merge(&1, updated_fields))
broadcast_event(ctx, "update", %{"fields" => updated_fields})
{:noreply, ctx}
end
def to_updates(_fields, name="data_from", value) do
property = @properties[name]
fields = %{name => ESCH.to_update(value, property[:type], Access.get(property, :opts))}
key = "traindata"
ESCH.update_key_with_module(fields, key, @inner_to_module[key], fn fields, key ->
fields["data_from"] == key
end)
end
def to_updates(_fields, name, value) do
property = @properties[name]
%{name => ESCH.to_update(value, property[:type], Access.get(property, :opts))}
end
@impl true
def to_attrs(%{assigns: %{fields: fields}}) do
fields
end
@impl true
def to_source(attrs) do
get_quoted_code(attrs)
|> Kino.SmartCell.quoted_to_string()
end
def get_quoted_code(attrs) do
quote do
unquote(ESCH.quoted_var(attrs["to_variable"])) =
Evision.ML.SVM.create()
|> Evision.ML.SVM.setType(unquote(ESCH.quoted_var("Evision.cv_#{attrs["type"]}()")))
|> Evision.ML.SVM.setKernel(unquote(ESCH.quoted_var("Evision.cv_#{attrs["kernel_type"]}()")))
unquote(set_svm_param(attrs))
unquote(set_kernel_param(attrs))
unquote(set_term_criteria(attrs))
unquote(train_on_dataset(attrs))
end
end
defp set_svm_param(attrs=%{"type" => "C_SVC"}) do
quote do
unquote(ESCH.quoted_var(attrs["to_variable"])) = Evision.ML.SVM.setC(unquote(ESCH.quoted_var(attrs["to_variable"])), unquote(attrs["c"]))
end
end
defp set_svm_param(attrs=%{"type" => "NU_SVC"}) do
quote do
unquote(ESCH.quoted_var(attrs["to_variable"])) = Evision.ML.SVM.setNu(unquote(ESCH.quoted_var(attrs["to_variable"])), unquote(attrs["nu"]))
end
end
defp set_svm_param(attrs=%{"type" => "ONE_CLASS"}) do
quote do
unquote(ESCH.quoted_var(attrs["to_variable"])) = Evision.ML.SVM.setNu(unquote(ESCH.quoted_var(attrs["to_variable"])), unquote(attrs["nu"]))
end
end
defp set_svm_param(attrs=%{"type" => "EPS_SVR"}) do
quote do
unquote(ESCH.quoted_var(attrs["to_variable"])) =
unquote(ESCH.quoted_var(attrs["to_variable"]))
|> Evision.ML.SVM.setC(unquote(attrs["c"]))
|> Evision.ML.SVM.setP(unquote(attrs["p"]))
end
end
defp set_svm_param(attrs=%{"type" => "NU_SVR"}) do
quote do
unquote(ESCH.quoted_var(attrs["to_variable"])) =
unquote(ESCH.quoted_var(attrs["to_variable"]))
|> Evision.ML.SVM.setC(unquote(attrs["c"]))
|> Evision.ML.SVM.setNu(unquote(attrs["nu"]))
end
end
defp set_svm_param(_) do
end
defp set_kernel_param(attrs=%{"kernel_type" => "POLY"}) do
quote do
unquote(ESCH.quoted_var(attrs["to_variable"])) =
unquote(ESCH.quoted_var(attrs["to_variable"]))
|> Evision.ML.SVM.setGamma(unquote(attrs["gamma"]))
|> Evision.ML.SVM.setCoef0(unquote(attrs["coef0"]))
|> Evision.ML.SVM.setDegree(unquote(attrs["degree"]))
end
end
defp set_kernel_param(attrs=%{"kernel_type" => "RBF"}) do
quote do
unquote(ESCH.quoted_var(attrs["to_variable"])) = Evision.ML.SVM.setGamma(unquote(ESCH.quoted_var(attrs["to_variable"])), unquote(attrs["gamma"]))
end
end
defp set_kernel_param(attrs=%{"kernel_type" => "SIGMOID"}) do
quote do
unquote(ESCH.quoted_var(attrs["to_variable"])) =
unquote(ESCH.quoted_var(attrs["to_variable"]))
|> Evision.ML.SVM.setGamma(unquote(attrs["gamma"]))
|> Evision.ML.SVM.setCoef0(unquote(attrs["coef0"]))
end
end
defp set_kernel_param(attrs=%{"kernel_type" => "CHI2"}) do
quote do
unquote(ESCH.quoted_var(attrs["to_variable"])) = Evision.ML.SVM.setGamma(unquote(ESCH.quoted_var(attrs["to_variable"])), unquote(attrs["gamma"]))
end
end
defp set_kernel_param(_) do
end
defp set_term_criteria(attrs=%{"term_criteria_type" => "max_count", "term_criteria_count" => count, "term_criteria_eps" => eps}) do
quote do
unquote(ESCH.quoted_var(attrs["to_variable"])) = Evision.ML.SVM.setTermCriteria(unquote(ESCH.quoted_var(attrs["to_variable"])), {Evision.cv_MAX_ITER(), unquote(count), unquote(eps)})
end
end
defp set_term_criteria(attrs=%{"term_criteria_type" => "eps", "term_criteria_count" => count, "term_criteria_eps" => eps}) do
quote do
unquote(ESCH.quoted_var(attrs["to_variable"])) = Evision.ML.SVM.setTermCriteria(unquote(ESCH.quoted_var(attrs["to_variable"])), {Evision.cv_EPS(), unquote(count), unquote(eps)})
end
end
defp set_term_criteria(attrs=%{"term_criteria_type" => "max_count+eps", "term_criteria_count" => count, "term_criteria_eps" => eps}) do
quote do
unquote(ESCH.quoted_var(attrs["to_variable"])) = Evision.ML.SVM.setTermCriteria(unquote(ESCH.quoted_var(attrs["to_variable"])), {Evision.cv_MAX_ITER() + Evision.cv_EPS(), unquote(count), unquote(eps)})
end
end
defp train_on_dataset(%{"data_from" => "traindata_var", "traindata_var" => traindata_var, "to_variable" => to_variable}) do
quote do
Evision.ML.SVM.train(unquote(ESCH.quoted_var(to_variable)), unquote(ESCH.quoted_var(traindata_var)))
unquote(TrainData.get_calc_error(Evision.ML.SVM, traindata_var, to_variable))
end
end
defp train_on_dataset(%{"data_from" => "traindata", "traindata" => traindata_attrs, "to_variable" => to_variable}) do
dataset_variable = traindata_attrs["to_variable"]
quote do
unquote(TrainData.get_quoted_code(traindata_attrs))
Evision.ML.SVM.train(unquote(ESCH.quoted_var(to_variable)), unquote(ESCH.quoted_var(dataset_variable)))
unquote(TrainData.get_calc_error(Evision.ML.SVM, dataset_variable, to_variable))
end
end
end
end