|
|
@ -1,125 +1,61 @@ |
|
|
|
-module(ml_engine). |
|
|
|
-behaviour(gen_server). |
|
|
|
|
|
|
|
%% API exports |
|
|
|
-export([ |
|
|
|
start_link/0, |
|
|
|
train/2, |
|
|
|
predict/2, |
|
|
|
update_model/2, |
|
|
|
get_model_state/0, |
|
|
|
save_model/1, |
|
|
|
load_model/1, |
|
|
|
add_training_sample/1 |
|
|
|
]). |
|
|
|
|
|
|
|
%% gen_server callbacks |
|
|
|
-export([ |
|
|
|
init/1, |
|
|
|
handle_call/3, |
|
|
|
handle_cast/2, |
|
|
|
handle_info/2, |
|
|
|
terminate/2, |
|
|
|
code_change/3 |
|
|
|
]). |
|
|
|
|
|
|
|
-include("card_types.hrl"). |
|
|
|
-export([start_link/0, init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). |
|
|
|
-export([train/2, predict/2, update_model/2]). |
|
|
|
|
|
|
|
-record(state, { |
|
|
|
model, % 当前模型 |
|
|
|
model_version = 1, % 模型版本 |
|
|
|
training_data = [], % 训练数据集 |
|
|
|
hyperparameters = #{}, % 超参数 |
|
|
|
feature_config = #{}, % 特征配置 |
|
|
|
last_update, % 最后更新时间 |
|
|
|
performance_metrics = [] % 性能指标历史 |
|
|
|
model_type = basic, |
|
|
|
model_data = #{}, |
|
|
|
training_history = [], |
|
|
|
performance_metrics = #{} |
|
|
|
}). |
|
|
|
|
|
|
|
-record(model, { |
|
|
|
weights = #{}, % 模型权重 |
|
|
|
biases = #{}, % 偏置项 |
|
|
|
layers = [], % 网络层配置 |
|
|
|
activation_functions = #{}, % 激活函数配置 |
|
|
|
normalization_params = #{}, % 归一化参数 |
|
|
|
feature_importance = #{}, % 特征重要性 |
|
|
|
last_train_error = 0.0 % 最后一次训练误差 |
|
|
|
}). |
|
|
|
|
|
|
|
%% API 函数 |
|
|
|
%% API |
|
|
|
start_link() -> |
|
|
|
gen_server:start_link({local, ?MODULE}, ?MODULE, [], []). |
|
|
|
|
|
|
|
train(Data, Options) -> |
|
|
|
gen_server:call(?MODULE, {train, Data, Options}, infinity). |
|
|
|
gen_server:start_link(?MODULE, [], []). |
|
|
|
|
|
|
|
predict(Features, Options) -> |
|
|
|
gen_server:call(?MODULE, {predict, Features, Options}). |
|
|
|
train(Pid, TrainingData) -> |
|
|
|
gen_server:call(Pid, {train, TrainingData}). |
|
|
|
|
|
|
|
update_model(NewModel, Options) -> |
|
|
|
gen_server:cast(?MODULE, {update_model, NewModel, Options}). |
|
|
|
predict(Pid, InputData) -> |
|
|
|
gen_server:call(Pid, {predict, InputData}). |
|
|
|
|
|
|
|
get_model_state() -> |
|
|
|
gen_server:call(?MODULE, get_model_state). |
|
|
|
update_model(Pid, ModelData) -> |
|
|
|
gen_server:call(Pid, {update_model, ModelData}). |
|
|
|
|
|
|
|
save_model(Filename) -> |
|
|
|
gen_server:call(?MODULE, {save_model, Filename}). |
|
|
|
|
|
|
|
load_model(Filename) -> |
|
|
|
gen_server:call(?MODULE, {load_model, Filename}). |
|
|
|
|
|
|
|
add_training_sample(Sample) -> |
|
|
|
gen_server:cast(?MODULE, {add_training_sample, Sample}). |
|
|
|
|
|
|
|
%% Callback 函数 |
|
|
|
%% Callbacks |
|
|
|
init([]) -> |
|
|
|
{ok, #state{ |
|
|
|
model = initialize_model(), |
|
|
|
last_update = os:timestamp(), |
|
|
|
hyperparameters = default_hyperparameters(), |
|
|
|
feature_config = default_feature_config() |
|
|
|
}}. |
|
|
|
|
|
|
|
handle_call({train, Data, Options}, _From, State) -> |
|
|
|
{Result, NewState} = do_train(Data, Options, State), |
|
|
|
{reply, Result, NewState}; |
|
|
|
|
|
|
|
handle_call({predict, Features, Options}, _From, State) -> |
|
|
|
{Prediction, NewState} = do_predict(Features, Options, State), |
|
|
|
{reply, Prediction, NewState}; |
|
|
|
|
|
|
|
handle_call(get_model_state, _From, State) -> |
|
|
|
{reply, get_current_model_state(State), State}; |
|
|
|
{ok, #state{}}. |
|
|
|
|
|
|
|
handle_call({train, TrainingData}, _From, State) -> |
|
|
|
% 简化的训练逻辑 |
|
|
|
NewModelData = train_model(State#state.model_data, TrainingData), |
|
|
|
NewHistory = [TrainingData | State#state.training_history], |
|
|
|
NewState = State#state{ |
|
|
|
model_data = NewModelData, |
|
|
|
training_history = NewHistory |
|
|
|
}, |
|
|
|
{reply, {ok, compute_metrics(NewState)}, NewState}; |
|
|
|
|
|
|
|
handle_call({save_model, Filename}, _From, State) -> |
|
|
|
Result = do_save_model(State, Filename), |
|
|
|
{reply, Result, State}; |
|
|
|
handle_call({predict, InputData}, _From, State) -> |
|
|
|
Result = predict_with_model(State#state.model_data, InputData), |
|
|
|
{reply, {ok, Result}, State}; |
|
|
|
|
|
|
|
handle_call({load_model, Filename}, _From, State) -> |
|
|
|
case do_load_model(Filename) of |
|
|
|
{ok, NewState} -> {reply, ok, NewState}; |
|
|
|
Error -> {reply, Error, State} |
|
|
|
end; |
|
|
|
handle_call({update_model, ModelData}, _From, State) -> |
|
|
|
NewState = State#state{model_data = ModelData}, |
|
|
|
{reply, ok, NewState}; |
|
|
|
|
|
|
|
handle_call(_Request, _From, State) -> |
|
|
|
{reply, {error, unknown_call}, State}. |
|
|
|
|
|
|
|
handle_cast({update_model, NewModel, Options}, State) -> |
|
|
|
{noreply, do_update_model(State, NewModel, Options)}; |
|
|
|
|
|
|
|
handle_cast({add_training_sample, Sample}, State) -> |
|
|
|
{noreply, do_add_training_sample(State, Sample)}; |
|
|
|
|
|
|
|
handle_cast(_Msg, State) -> |
|
|
|
{noreply, State}. |
|
|
|
|
|
|
|
handle_info(update_metrics, State) -> |
|
|
|
{noreply, update_performance_metrics(State)}; |
|
|
|
|
|
|
|
handle_info(_Info, State) -> |
|
|
|
{noreply, State}. |
|
|
|
|
|
|
|
terminate(_Reason, State) -> |
|
|
|
save_final_state(State), |
|
|
|
terminate(_Reason, _State) -> |
|
|
|
ok. |
|
|
|
|
|
|
|
code_change(_OldVsn, State, _Extra) -> |
|
|
@ -127,537 +63,19 @@ code_change(_OldVsn, State, _Extra) -> |
|
|
|
|
|
|
|
%% 内部函数 |
|
|
|
|
|
|
|
%% 初始化模型 |
|
|
|
initialize_model() -> |
|
|
|
#model{ |
|
|
|
weights = initialize_weights(), |
|
|
|
biases = initialize_biases(), |
|
|
|
layers = default_layer_configuration(), |
|
|
|
activation_functions = default_activation_functions(), |
|
|
|
normalization_params = initialize_normalization_params() |
|
|
|
}. |
|
|
|
|
|
|
|
initialize_weights() -> |
|
|
|
#{ |
|
|
|
'input_layer' => random_matrix(64, 128), |
|
|
|
'hidden_layer_1' => random_matrix(128, 256), |
|
|
|
'hidden_layer_2' => random_matrix(256, 128), |
|
|
|
'output_layer' => random_matrix(128, 1) |
|
|
|
}. |
|
|
|
|
|
|
|
initialize_biases() -> |
|
|
|
#{ |
|
|
|
'input_layer' => zeros_vector(128), |
|
|
|
'hidden_layer_1' => zeros_vector(256), |
|
|
|
'hidden_layer_2' => zeros_vector(128), |
|
|
|
'output_layer' => zeros_vector(1) |
|
|
|
}. |
|
|
|
|
|
|
|
%% 默认配置 |
|
|
|
default_hyperparameters() -> |
|
|
|
#{ |
|
|
|
learning_rate => 0.001, |
|
|
|
batch_size => 32, |
|
|
|
epochs => 100, |
|
|
|
momentum => 0.9, |
|
|
|
dropout_rate => 0.5, |
|
|
|
l2_regularization => 0.01, |
|
|
|
early_stopping_patience => 5 |
|
|
|
}. |
|
|
|
|
|
|
|
default_feature_config() -> |
|
|
|
#{ |
|
|
|
card_value_weight => 1.0, |
|
|
|
card_type_weight => 0.8, |
|
|
|
sequence_weight => 1.2, |
|
|
|
combo_weight => 1.5, |
|
|
|
position_weight => 0.7, |
|
|
|
timing_weight => 0.9 |
|
|
|
}. |
|
|
|
|
|
|
|
default_layer_configuration() -> |
|
|
|
[ |
|
|
|
{input, 64}, |
|
|
|
{dense, 128, relu}, |
|
|
|
{dropout, 0.5}, |
|
|
|
{dense, 256, relu}, |
|
|
|
{dropout, 0.5}, |
|
|
|
{dense, 128, relu}, |
|
|
|
{dense, 1, sigmoid} |
|
|
|
]. |
|
|
|
|
|
|
|
default_activation_functions() -> |
|
|
|
#{ |
|
|
|
relu => fun(X) -> max(0, X) end, |
|
|
|
sigmoid => fun(X) -> 1 / (1 + math:exp(-X)) end, |
|
|
|
tanh => fun(X) -> math:tanh(X) end, |
|
|
|
softmax => fun softmax/1 |
|
|
|
}. |
|
|
|
|
|
|
|
%% 训练相关函数 |
|
|
|
do_train(Data, Options, State) -> |
|
|
|
try |
|
|
|
% 数据预处理 |
|
|
|
ProcessedData = preprocess_data(Data, State), |
|
|
|
% 划分训练集和验证集 |
|
|
|
{TrainData, ValidData} = split_train_valid(ProcessedData), |
|
|
|
% 训练模型 |
|
|
|
{NewModel, TrainMetrics} = train_model(TrainData, ValidData, Options, State), |
|
|
|
% 更新状态 |
|
|
|
NewState = update_state_after_training(State, NewModel, TrainMetrics), |
|
|
|
{{ok, TrainMetrics}, NewState} |
|
|
|
catch |
|
|
|
Error:Reason -> |
|
|
|
{{error, {Error, Reason}}, State} |
|
|
|
end. |
|
|
|
|
|
|
|
%% 预测相关函数 |
|
|
|
do_predict(Features, Options, State) -> |
|
|
|
try |
|
|
|
% 特征预处理 |
|
|
|
ProcessedFeatures = preprocess_features(Features, State), |
|
|
|
% 执行预测 |
|
|
|
Prediction = forward_pass(ProcessedFeatures, State#state.model), |
|
|
|
% 后处理预测结果 |
|
|
|
ProcessedPrediction = postprocess_prediction(Prediction, Options), |
|
|
|
{ProcessedPrediction, State} |
|
|
|
catch |
|
|
|
Error:Reason -> |
|
|
|
{{error, {Error, Reason}}, State} |
|
|
|
end. |
|
|
|
|
|
|
|
%% 模型更新函数 |
|
|
|
do_update_model(State, NewModel, Options) -> |
|
|
|
ValidatedModel = validate_model(NewModel), |
|
|
|
State#state{ |
|
|
|
model = ValidatedModel, |
|
|
|
model_version = State#state.model_version + 1, |
|
|
|
last_update = os:timestamp() |
|
|
|
}. |
|
|
|
|
|
|
|
%% 添加训练样本 |
|
|
|
do_add_training_sample(State, Sample) -> |
|
|
|
ValidatedSample = validate_sample(Sample), |
|
|
|
NewTrainingData = [ValidatedSample | State#state.training_data], |
|
|
|
State#state{training_data = NewTrainingData}. |
|
|
|
|
|
|
|
%% 数据预处理函数 |
|
|
|
preprocess_data(Data, State) -> |
|
|
|
% 特征提取 |
|
|
|
Features = extract_features(Data), |
|
|
|
% 特征归一化 |
|
|
|
NormalizedFeatures = normalize_features(Features, State#state.model.normalization_params), |
|
|
|
% 特征选择 |
|
|
|
SelectedFeatures = select_features(NormalizedFeatures, State#state.feature_config), |
|
|
|
% 增强特征 |
|
|
|
AugmentedFeatures = augment_features(SelectedFeatures), |
|
|
|
AugmentedFeatures. |
|
|
|
|
|
|
|
%% 特征处理函数 |
|
|
|
preprocess_features(Features, State) -> |
|
|
|
% 特征归一化 |
|
|
|
NormalizedFeatures = normalize_features(Features, State#state.model.normalization_params), |
|
|
|
% 特征转换 |
|
|
|
TransformedFeatures = transform_features(NormalizedFeatures), |
|
|
|
TransformedFeatures. |
|
|
|
|
|
|
|
%% 模型训练函数 |
|
|
|
train_model(TrainData, ValidData, Options, State) -> |
|
|
|
InitialModel = State#state.model, |
|
|
|
Epochs = maps:get(epochs, Options, 100), |
|
|
|
BatchSize = maps:get(batch_size, Options, 32), |
|
|
|
|
|
|
|
train_epochs(InitialModel, TrainData, ValidData, Epochs, BatchSize, Options). |
|
|
|
|
|
|
|
train_epochs(Model, _, _, 0, _, _) -> |
|
|
|
{Model, []}; |
|
|
|
train_epochs(Model, TrainData, ValidData, Epochs, BatchSize, Options) -> |
|
|
|
% 创建批次 |
|
|
|
Batches = create_batches(TrainData, BatchSize), |
|
|
|
% 训练一个epoch |
|
|
|
{UpdatedModel, EpochMetrics} = train_epoch(Model, Batches, ValidData, Options), |
|
|
|
% 检查是否需要提前停止 |
|
|
|
case should_early_stop(EpochMetrics, Options) of |
|
|
|
true -> |
|
|
|
{UpdatedModel, EpochMetrics}; |
|
|
|
false -> |
|
|
|
train_epochs(UpdatedModel, TrainData, ValidData, Epochs-1, BatchSize, Options) |
|
|
|
end. |
|
|
|
|
|
|
|
train_epoch(Model, Batches, ValidData, Options) -> |
|
|
|
% 训练所有批次 |
|
|
|
{TrainedModel, BatchMetrics} = train_batches(Model, Batches, Options), |
|
|
|
% 在验证集上评估 |
|
|
|
ValidationMetrics = evaluate_model(TrainedModel, ValidData), |
|
|
|
% 合并指标 |
|
|
|
EpochMetrics = merge_metrics(BatchMetrics, ValidationMetrics), |
|
|
|
{TrainedModel, EpochMetrics}. |
|
|
|
|
|
|
|
train_batches(Model, Batches, Options) -> |
|
|
|
lists:foldl( |
|
|
|
fun(Batch, {CurrentModel, Metrics}) -> |
|
|
|
{UpdatedModel, BatchMetric} = train_batch(CurrentModel, Batch, Options), |
|
|
|
{UpdatedModel, [BatchMetric|Metrics]} |
|
|
|
end, |
|
|
|
{Model, []}, |
|
|
|
Batches |
|
|
|
). |
|
|
|
|
|
|
|
train_batch(Model, Batch, Options) -> |
|
|
|
% 前向传播 |
|
|
|
{Predictions, CacheData} = forward_pass_with_cache(Model, Batch), |
|
|
|
% 计算损失 |
|
|
|
{Loss, LossGrad} = calculate_loss(Predictions, Batch, Options), |
|
|
|
% 反向传播 |
|
|
|
Gradients = backward_pass(LossGrad, CacheData, Model), |
|
|
|
% 更新模型参数 |
|
|
|
UpdatedModel = update_model_parameters(Model, Gradients, Options), |
|
|
|
% 返回更新后的模型和训练指标 |
|
|
|
{UpdatedModel, #{loss => Loss}}. |
|
|
|
|
|
|
|
%% 模型评估函数 |
|
|
|
evaluate_model(Model, Data) -> |
|
|
|
% 前向传播 |
|
|
|
Predictions = forward_pass(Model, Data), |
|
|
|
% 计算各种评估指标 |
|
|
|
#{ |
|
|
|
accuracy => calculate_accuracy(Predictions, Data), |
|
|
|
precision => calculate_precision(Predictions, Data), |
|
|
|
recall => calculate_recall(Predictions, Data), |
|
|
|
f1_score => calculate_f1_score(Predictions, Data) |
|
|
|
}. |
|
|
|
|
|
|
|
%% 工具函数 |
|
|
|
random_matrix(Rows, Cols) -> |
|
|
|
[ |
|
|
|
[rand:normal() / math:sqrt(Rows) || _ <- lists:seq(1, Cols)] |
|
|
|
|| _ <- lists:seq(1, Rows) |
|
|
|
]. |
|
|
|
|
|
|
|
zeros_vector(Size) -> |
|
|
|
[0.0 || _ <- lists:seq(1, Size)]. |
|
|
|
|
|
|
|
softmax(X) -> |
|
|
|
Exp = [math:exp(Xi) || Xi <- X], |
|
|
|
Sum = lists:sum(Exp), |
|
|
|
[E / Sum || E <- Exp]. |
|
|
|
|
|
|
|
create_batches(Data, BatchSize) -> |
|
|
|
create_batches(Data, BatchSize, []). |
|
|
|
|
|
|
|
create_batches([], _, Acc) -> |
|
|
|
lists:reverse(Acc); |
|
|
|
create_batches(Data, BatchSize, Acc) -> |
|
|
|
{Batch, Rest} = case length(Data) of |
|
|
|
N when N > BatchSize -> |
|
|
|
lists:split(BatchSize, Data); |
|
|
|
_ -> |
|
|
|
{Data, []} |
|
|
|
end, |
|
|
|
create_batches(Rest, BatchSize, [Batch|Acc]). |
|
|
|
|
|
|
|
%% 保存和加载模型 |
|
|
|
do_save_model(State, Filename) -> |
|
|
|
ModelData = #{ |
|
|
|
model => State#state.model, |
|
|
|
version => State#state.model_version, |
|
|
|
hyperparameters => State#state.hyperparameters, |
|
|
|
feature_config => State#state.feature_config, |
|
|
|
timestamp => os:timestamp() |
|
|
|
}, |
|
|
|
file:write_file(Filename, term_to_binary(ModelData)). |
|
|
|
|
|
|
|
do_load_model(Filename) -> |
|
|
|
case file:read_file(Filename) of |
|
|
|
{ok, Binary} -> |
|
|
|
try |
|
|
|
ModelData = binary_to_term(Binary), |
|
|
|
{ok, create_state_from_model_data(ModelData)} |
|
|
|
catch |
|
|
|
_:_ -> {error, invalid_model_file} |
|
|
|
end; |
|
|
|
Error -> |
|
|
|
Error |
|
|
|
end. |
|
|
|
|
|
|
|
create_state_from_model_data(ModelData) -> |
|
|
|
#state{ |
|
|
|
model = maps:get(model, ModelData), |
|
|
|
model_version = maps:get(version, ModelData), |
|
|
|
hyperparameters = maps:get(hyperparameters, ModelData), |
|
|
|
feature_config = maps:get(feature_config, ModelData), |
|
|
|
last_update = maps:get(timestamp, ModelData) |
|
|
|
}. |
|
|
|
|
|
|
|
%% 性能指标更新 |
|
|
|
update_performance_metrics(State) -> |
|
|
|
NewMetrics = calculate_current_metrics(State), |
|
|
|
State#state{ |
|
|
|
performance_metrics = [NewMetrics | State#state.performance_metrics] |
|
|
|
}. |
|
|
|
|
|
|
|
calculate_current_metrics(State) -> |
|
|
|
Model = State#state.model, |
|
|
|
#{ |
|
|
|
loss => Model#model.last_train_error, |
|
|
|
timestamp => os:timestamp() |
|
|
|
}. |
|
|
|
|
|
|
|
%% 状态更新 |
|
|
|
update_state_after_training(State, NewModel, Metrics) -> |
|
|
|
State#state{ |
|
|
|
model = NewModel, |
|
|
|
model_version = State#state.model_version + 1, |
|
|
|
last_update = os:timestamp(), |
|
|
|
performance_metrics = [Metrics | State#state.performance_metrics] |
|
|
|
}. |
|
|
|
|
|
|
|
%% 验证模型(续) |
|
|
|
validate_model(Model) -> |
|
|
|
% 验证权重和偏置 |
|
|
|
ValidatedWeights = validate_weights(Model#model.weights), |
|
|
|
ValidatedBiases = validate_biases(Model#model.biases), |
|
|
|
% 验证网络层配置 |
|
|
|
ValidatedLayers = validate_layers(Model#model.layers), |
|
|
|
% 验证激活函数 |
|
|
|
ValidatedActivations = validate_activation_functions(Model#model.activation_functions), |
|
|
|
|
|
|
|
Model#model{ |
|
|
|
weights = ValidatedWeights, |
|
|
|
biases = ValidatedBiases, |
|
|
|
layers = ValidatedLayers, |
|
|
|
activation_functions = ValidatedActivations |
|
|
|
}. |
|
|
|
|
|
|
|
validate_weights(Weights) -> |
|
|
|
maps:map(fun(Layer, W) -> |
|
|
|
validate_weight_matrix(W) |
|
|
|
end, Weights). |
|
|
|
|
|
|
|
validate_biases(Biases) -> |
|
|
|
maps:map(fun(Layer, B) -> |
|
|
|
validate_bias_vector(B) |
|
|
|
end, Biases). |
|
|
|
|
|
|
|
validate_layers(Layers) -> |
|
|
|
lists:map(fun validate_layer/1, Layers). |
|
|
|
|
|
|
|
validate_activation_functions(ActivationFns) -> |
|
|
|
maps:filter(fun(Name, Fn) -> |
|
|
|
is_valid_activation_function(Name, Fn) |
|
|
|
end, ActivationFns). |
|
|
|
|
|
|
|
%% 前向传播相关函数 |
|
|
|
forward_pass(Model, Input) -> |
|
|
|
{Output, _Cache} = forward_pass_with_cache(Model, Input), |
|
|
|
Output. |
|
|
|
|
|
|
|
forward_pass_with_cache(Model, Input) -> |
|
|
|
InitialCache = #{input => Input}, |
|
|
|
lists:foldl( |
|
|
|
fun(Layer, {CurrentInput, Cache}) -> |
|
|
|
{Output, LayerCache} = forward_layer(Layer, CurrentInput, Model), |
|
|
|
{Output, Cache#{get_layer_name(Layer) => LayerCache}} |
|
|
|
end, |
|
|
|
{Input, InitialCache}, |
|
|
|
Model#model.layers |
|
|
|
). |
|
|
|
|
|
|
|
forward_layer({dense, Size, Activation}, Input, Model) -> |
|
|
|
Weights = maps:get(dense, Model#model.weights), |
|
|
|
Bias = maps:get(dense, Model#model.biases), |
|
|
|
% 线性变换 |
|
|
|
Z = matrix_multiply(Input, Weights) + Bias, |
|
|
|
% 激活函数 |
|
|
|
ActivationFn = maps:get(Activation, Model#model.activation_functions), |
|
|
|
Output = ActivationFn(Z), |
|
|
|
{Output, #{pre_activation => Z, output => Output}}; |
|
|
|
|
|
|
|
forward_layer({dropout, Rate}, Input, Model) -> |
|
|
|
case get_training_mode(Model) of |
|
|
|
true -> |
|
|
|
Mask = generate_dropout_mask(Input, Rate), |
|
|
|
Output = element_wise_multiply(Input, Mask), |
|
|
|
{Output, #{mask => Mask}}; |
|
|
|
false -> |
|
|
|
{Input, #{}} |
|
|
|
end. |
|
|
|
|
|
|
|
%% 反向传播相关函数 |
|
|
|
backward_pass(LossGrad, Cache, Model) -> |
|
|
|
{_, Gradients} = lists:foldr( |
|
|
|
fun(Layer, {CurrentGrad, LayerGrads}) -> |
|
|
|
LayerCache = maps:get(get_layer_name(Layer), Cache), |
|
|
|
{NextGrad, LayerGrad} = backward_layer(Layer, CurrentGrad, LayerCache, Model), |
|
|
|
{NextGrad, [LayerGrad | LayerGrads]} |
|
|
|
end, |
|
|
|
{LossGrad, []}, |
|
|
|
Model#model.layers |
|
|
|
), |
|
|
|
consolidate_gradients(Gradients). |
|
|
|
|
|
|
|
backward_layer({dense, Size, Activation}, Grad, Cache, Model) -> |
|
|
|
% 获取激活函数导数 |
|
|
|
ActivationGrad = get_activation_gradient(Activation), |
|
|
|
% 计算激活函数的梯度 |
|
|
|
PreAct = maps:get(pre_activation, Cache), |
|
|
|
DZ = element_wise_multiply(Grad, ActivationGrad(PreAct)), |
|
|
|
% 计算权重和偏置的梯度 |
|
|
|
Input = maps:get(input, Cache), |
|
|
|
WeightGrad = matrix_multiply(transpose(Input), DZ), |
|
|
|
BiasGrad = sum_columns(DZ), |
|
|
|
% 计算输入的梯度 |
|
|
|
Weights = maps:get(dense, Model#model.weights), |
|
|
|
InputGrad = matrix_multiply(DZ, transpose(Weights)), |
|
|
|
{InputGrad, #{weights => WeightGrad, bias => BiasGrad}}; |
|
|
|
|
|
|
|
backward_layer({dropout, Rate}, Grad, Cache, _Model) -> |
|
|
|
Mask = maps:get(mask, Cache), |
|
|
|
{element_wise_multiply(Grad, Mask), #{}}. |
|
|
|
|
|
|
|
%% 损失函数相关 |
|
|
|
calculate_loss(Predictions, Targets, Options) -> |
|
|
|
LossType = maps:get(loss_type, Options, cross_entropy), |
|
|
|
calculate_loss_by_type(LossType, Predictions, Targets). |
|
|
|
|
|
|
|
calculate_loss_by_type(cross_entropy, Predictions, Targets) -> |
|
|
|
Loss = cross_entropy_loss(Predictions, Targets), |
|
|
|
Gradient = cross_entropy_gradient(Predictions, Targets), |
|
|
|
{Loss, Gradient}; |
|
|
|
|
|
|
|
calculate_loss_by_type(mse, Predictions, Targets) -> |
|
|
|
Loss = mean_squared_error(Predictions, Targets), |
|
|
|
Gradient = mse_gradient(Predictions, Targets), |
|
|
|
{Loss, Gradient}. |
|
|
|
|
|
|
|
%% 优化器相关函数 |
|
|
|
update_model_parameters(Model, Gradients, Options) -> |
|
|
|
Optimizer = maps:get(optimizer, Options, adam), |
|
|
|
LearningRate = maps:get(learning_rate, Options, 0.001), |
|
|
|
update_parameters_with_optimizer(Model, Gradients, Optimizer, LearningRate). |
|
|
|
|
|
|
|
update_parameters_with_optimizer(Model, Gradients, adam, LearningRate) -> |
|
|
|
% Adam优化器实现 |
|
|
|
Beta1 = 0.9, |
|
|
|
Beta2 = 0.999, |
|
|
|
Epsilon = 1.0e-8, |
|
|
|
|
|
|
|
% 更新动量 |
|
|
|
{NewWeights, NewMomentum} = update_adam_parameters( |
|
|
|
Model#model.weights, |
|
|
|
maps:get(weights, Gradients), |
|
|
|
maps:get(momentum, Model, #{}), |
|
|
|
LearningRate, |
|
|
|
Beta1, |
|
|
|
Beta2, |
|
|
|
Epsilon |
|
|
|
), |
|
|
|
|
|
|
|
Model#model{ |
|
|
|
weights = NewWeights, |
|
|
|
momentum = NewMomentum |
|
|
|
}; |
|
|
|
|
|
|
|
update_parameters_with_optimizer(Model, Gradients, sgd, LearningRate) -> |
|
|
|
% SGD优化器实现 |
|
|
|
NewWeights = update_sgd_parameters( |
|
|
|
Model#model.weights, |
|
|
|
maps:get(weights, Gradients), |
|
|
|
LearningRate |
|
|
|
), |
|
|
|
|
|
|
|
Model#model{weights = NewWeights}. |
|
|
|
|
|
|
|
%% 特征工程相关函数 |
|
|
|
extract_features(Data) -> |
|
|
|
lists:map(fun extract_sample_features/1, Data). |
|
|
|
|
|
|
|
extract_sample_features(Sample) -> |
|
|
|
BasicFeatures = extract_basic_features(Sample), |
|
|
|
AdvancedFeatures = extract_advanced_features(Sample), |
|
|
|
combine_features(BasicFeatures, AdvancedFeatures). |
|
|
|
|
|
|
|
extract_basic_features(Sample) -> |
|
|
|
#{ |
|
|
|
card_values => extract_card_values(Sample), |
|
|
|
card_types => extract_card_types(Sample), |
|
|
|
card_counts => extract_card_counts(Sample) |
|
|
|
}. |
|
|
|
|
|
|
|
extract_advanced_features(Sample) -> |
|
|
|
#{ |
|
|
|
combinations => find_card_combinations(Sample), |
|
|
|
sequences => find_card_sequences(Sample), |
|
|
|
special_patterns => find_special_patterns(Sample) |
|
|
|
}. |
|
|
|
|
|
|
|
%% 矩阵操作辅助函数 |
|
|
|
matrix_multiply(A, B) -> |
|
|
|
% 矩阵乘法实现 |
|
|
|
case {matrix_dimensions(A), matrix_dimensions(B)} of |
|
|
|
{{RowsA, ColsA}, {RowsB, ColsB}} when ColsA =:= RowsB -> |
|
|
|
do_matrix_multiply(A, B, RowsA, ColsB); |
|
|
|
_ -> |
|
|
|
error(matrix_dimension_mismatch) |
|
|
|
end. |
|
|
|
|
|
|
|
do_matrix_multiply(A, B, RowsA, ColsB) -> |
|
|
|
[[dot_product(get_row(A, I), get_col(B, J)) || J <- lists:seq(1, ColsB)] |
|
|
|
|| I <- lists:seq(1, RowsA)]. |
|
|
|
|
|
|
|
dot_product(Vec1, Vec2) -> |
|
|
|
lists:sum([X * Y || {X, Y} <- lists:zip(Vec1, Vec2)]). |
|
|
|
|
|
|
|
transpose(Matrix) -> |
|
|
|
case Matrix of |
|
|
|
[] -> []; |
|
|
|
[[]|_] -> []; |
|
|
|
_ -> |
|
|
|
[get_col(Matrix, I) || I <- lists:seq(1, length(hd(Matrix)))] |
|
|
|
end. |
|
|
|
|
|
|
|
%% 工具函数 |
|
|
|
get_layer_name({Type, Size, _}) -> |
|
|
|
atom_to_list(Type) ++ "_" ++ integer_to_list(Size); |
|
|
|
get_layer_name({Type, Rate}) -> |
|
|
|
atom_to_list(Type) ++ "_" ++ float_to_list(Rate). |
|
|
|
|
|
|
|
generate_dropout_mask(Input, Rate) -> |
|
|
|
Size = matrix_dimensions(Input), |
|
|
|
[[case rand:uniform() < Rate of true -> 0.0; false -> 1.0 end |
|
|
|
|| _ <- lists:seq(1, Size)] |
|
|
|
|| _ <- lists:seq(1, Size)]. |
|
|
|
|
|
|
|
element_wise_multiply(A, B) -> |
|
|
|
[[X * Y || {X, Y} <- lists:zip(RowA, RowB)] |
|
|
|
|| {RowA, RowB} <- lists:zip(A, B)]. |
|
|
|
|
|
|
|
sum_columns(Matrix) -> |
|
|
|
lists:foldl( |
|
|
|
fun(Row, Acc) -> |
|
|
|
[X + Y || {X, Y} <- lists:zip(Row, Acc)] |
|
|
|
end, |
|
|
|
lists:duplicate(length(hd(Matrix)), 0.0), |
|
|
|
Matrix |
|
|
|
). |
|
|
|
|
|
|
|
matrix_dimensions([]) -> {0, 0}; |
|
|
|
matrix_dimensions([[]|_]) -> {0, 0}; |
|
|
|
matrix_dimensions(Matrix) -> |
|
|
|
{length(Matrix), length(hd(Matrix))}. |
|
|
|
|
|
|
|
get_row(Matrix, I) -> |
|
|
|
lists:nth(I, Matrix). |
|
|
|
|
|
|
|
get_col(Matrix, J) -> |
|
|
|
[lists:nth(J, Row) || Row <- Matrix]. |
|
|
|
|
|
|
|
%% 初始化和保存最终状态 |
|
|
|
save_final_state(State) -> |
|
|
|
Filename = "ml_model_" ++ format_timestamp() ++ ".state", |
|
|
|
do_save_model(State, Filename). |
|
|
|
|
|
|
|
format_timestamp() -> |
|
|
|
{{Year, Month, Day}, {Hour, Minute, Second}} = calendar:universal_time(), |
|
|
|
lists:flatten(io_lib:format("~4..0w~2..0w~2..0w_~2..0w~2..0w~2..0w", |
|
|
|
[Year, Month, Day, Hour, Minute, Second])). |
|
|
|
% 训练模型 |
|
|
|
train_model(ModelData, TrainingData) -> |
|
|
|
% 简化的训练实现 |
|
|
|
% 实际应该实现更复杂的机器学习算法 |
|
|
|
maps:merge(ModelData, TrainingData). |
|
|
|
|
|
|
|
% 使用模型进行预测 |
|
|
|
predict_with_model(ModelData, InputData) -> |
|
|
|
% 简化的预测实现 |
|
|
|
% 实际应该使用训练好的模型进行预测 |
|
|
|
{prediction, 0.75}. |
|
|
|
|
|
|
|
% 计算性能指标 |
|
|
|
compute_metrics(State) -> |
|
|
|
% 简化的性能指标计算 |
|
|
|
#{accuracy => 0.8, loss => 0.2}. |