机器学习模型评估与分类算法实现
立即解锁
发布时间: 2025-09-04 01:36:33 阅读量: 12 订阅数: 8 AIGC 


Rust机器学习实战
### 机器学习模型评估与分类算法实现
在机器学习中,评估模型的性能以及选择合适的分类算法至关重要。下面将详细介绍回归模型的评估方法以及多种分类算法的实现。
#### 回归模型评估
在机器学习模型的代码中,我们使用了一些函数来评估模型的好坏,如 `neg_mean_squared_error` 或 `r_squared_score`。评估模型对于判断其在新数据和未来数据上的预测能力非常重要。由于未来数据的目标值未知,我们需要在已知数据上检查模型的准确率指标。因此,在训练前,我们会将数据集划分为训练集和测试集,以此来评估模型与实际应用场景的接近程度。
##### MAE 和 MSE
对于连续变量,最常用的评估指标是平均绝对误差(MAE)和均方误差(MSE)。
- **MAE**:是预测值与观测值之间绝对差的平均值。公式如下:
\[
MAE = \frac{1}{n} \sum_{i=1}^{n} |\hat{y}_i - y_i|
\]
- **MSE**:是预测值与观测值之间差异的平方和(也称为残差)。公式如下:
\[
MSE = \frac{1}{n} \sum_{i=1}^{n} (\hat{y}_i - y_i)^2
\]
在很多情况下,还会取 MSE 的平方根,即均方根误差(RMSE)。RMSE 在处理高斯分布时是最好的误差估计器,但大多数实际应用场景并非严格的高斯分布,取残差的平方会过度关注异常值。MAE 更易于理解,对异常值的关注较少。然而,RMSE 是许多模型的默认指标,因为基于 RMSE 定义的损失函数具有平滑的可微性,便于进行机器学习所需的数学运算。
在 `rusty machine` 中,训练完成后,我们可以通过以下代码获取模型的均方误差:
```rust
use rusty_machine::analysis::score::neg_mean_squared_error;
pub fn run() -> Result<(), Box<dyn Error>> {
// previous part ...
let predictions = lin_model.predict(&boston_x_test).unwrap();
let predictions = Matrix::new(test_size, 1, predictions);
let acc = neg_mean_squared_error(&predictions, &boston_y_test);
println!("linear regression error: {:?}", acc);
let predictions = gaus_model.predict(&boston_x_test).unwrap();
let predictions = Matrix::new(test_size, 1, predictions);
let acc = neg_mean_squared_error(&predictions, &boston_y_test);
println!("gaussian process regression error: {:?}", acc);
// remaining code...
}
```
##### R - 平方误差
R - 平方用于评估数据点围绕拟合回归线的离散程度,也称为决定系数。对于同一数据集,较高的 R - 平方值表示观测值与拟合值之间的差异较小。R - 平方是线性模型解释的因变量变化的百分比,公式如下:
\[
R^2 = \frac{Variance\ explained\ by\ the\ model}{Total\ variance} = 1 - \frac{\sum_{i=1}^{n} (y_i - \hat{y}_i)^2}{\sum_{i=1}^{n} (y_i - \bar{y})^2}
\]
以下是在 Rust 中实现 R - 平方的代码:
```rust
fn r_squared_score(y_test: &Vec<f64>, y_preds: &Vec<f64>) -> f64 {
let mv: f64 = y_test.iter().zip(y_preds.iter()).fold(
0., |v, (y_i, y_i_hat)| {
v + (y_i - y_i_hat).powi(2)
}
);
let mean = y_test.iter().sum::<f64>() as f64
/ y_test.len() as f64;
let var = y_test.iter().fold(
0., |v, &x| {v + (x - mean).powi(2)}
);
let r2: f64 = 1.0 - (mv / var);
r2
}
```
在实际应用中,我们可以这样使用:
```rust
pub fn run() -> Result<(), Box<dyn Error>> {
// previous part of the function...
println!("glm poisson R2 score: {:?}", r_squared_score(
&boston_y_test.data(), &predictions.data()));
}
```
#### 分类算法
当机器学习算法需要将输入变量分类到预定义的类别中时,这类问题被称为分类问题。下面将介绍如何在 Rust 中创建分类模型。
##### Iris 数据集
为了展示分类算法的使用,我们将使用 Iris 数据集。该数据集是一个多变量数据集,包含以下特征:
- 萼片长度(cm)
- 萼片宽度(cm)
- 花瓣长度(cm)
- 花瓣宽度(cm)
- 类别:setosa、versicolor 和 virginica
以下是相关代码的操作步骤:
1. 在 `rustlearn_classification_tasks` 文件夹中创建 `data` 文件夹,并将 `iris.csv` 文件放在其中。
2. 在 `Cargo.toml` 文件中添加依赖:
```toml
[package]
name = "rustlearn_classification_tasks"
version = "0.1.0"
edition = "2018"
[dependencies]
rustlearn = "0.5.0"
csv = "1.0.5"
serde = "1.0.89"
serde_derive = "1.0.89"
rand = "0.6"
ml-utils = { path = "../ml-utils" }
```
3. 创建 `Flower` 结构体:
```rust
extern crate serde;
#[macro_use]
extern crate serde_derive;
#[derive(Debug, Deserialize)]
pub struct Flower {
sepal_length: f32, sepal_width: f32,
petal_length: f32, petal_width: f32,
species: String,
}
```
4. 实现 `into_feature_vector` 和 `into_labels` 方法:
```rust
use std::io; use std::vec::Vec; use csv;
impl Flower {
pub fn into_feature_vector(&self) -> Vec<f32> {
vec![self.sepal_length, self.sepal_width,
self.petal_length, self.petal_width]
}
pub fn into_labels(&self) -> f32 {
match self.species.as_str() {
"setosa" => 0., "versicolor" => 1.,
"virginica" => 2.,
some_other => panic!("Not able to parse the label.
Some other label got passed. {:?}", some_other),
}
}
}
```
5. 读取数据并进行预处理:
```rust
use ml_utils::datasets::Flower;
use csv; use rand::thread_rng;
use rand::seq::SliceRandom;
pub fn run() -> Result<(), Box<dyn Error>> {
let mut rdr = csv::Reader::from_reader(io::stdin());
let mut data = Vec::new();
for result in rdr.deserialize() {
let r: Flower = result?;
data.push(r);
}
data.shuffle(&mut thread_rng());
// rest of the code...
}
```
6. 分离训练集和测试集:
```rust
use rustlearn::prelude::*;
pub fn run() -> Result<(), Box<dyn Error>> {
// previous part of the function ...
// separate out to train and test datasets.
let test_size: f32 = 0.2;
let test_size: f32 = data.len() as f32 * test_size;
let test_size = test_size.round() as usize;
let (test_data, train_data) = data.split_at(test_size);
let train_size = train_data.len();
let test_size = test_data.len();
// differentiate the features and the labels.
let flower_x_train: Vec<f32> = train_data.iter()
.flat_map(|r| r.into_feature_vector()).collect();
let flower_y_train: Vec<f32> = train_data.iter()
.map(|r| r.into_labels()).collect();
let flower_x_test: Vec<f32> = test_data.iter()
.flat_map(|r| r.into_feature_vector()).collect();
let flower_y_test: Vec<f32> = test_data.iter()
.map(|r| r.into_labels()).collect();
// Convert the vectors to a dense matrix or a sparse matrix
let mut flower_x_train = Array::from(flower_x_train);
flower_x_train.reshape(train_size, 4);
let flower_y_train = Array::from(flower_y_train);
let mut flower_x_test = Array::from(flower_x_test);
flower_x_test.reshape(test_size, 4);
// rest of the function ...
}
```
以下是整个数据处理流程的 mermaid 流程图:
```mermaid
graph LR
A[读取 Iris 数据集] --> B[创建 Flower 结构体]
B --> C[实现特征和标签提取方法]
C --> D[读取数据并打乱]
D --> E[分离训练集和测试集]
E --> F[转换为矩阵]
```
通过以上步骤,我们完成了回归模型的评估以及 Iris 数据集的预处理,为后续的分类算法实现奠定了基础。接下来,我们将介绍几种常见的分类算法及其在 Rust 中的实现。
### 机器学习模型评估与分类算法实现
##### 逻辑回归
逻辑回归是一种流行的分类技术,它使用对数几率函数来建模二元因变量,假设因变量服从伯努利分布。与普通最小二乘法(OLS)回归不同,逻辑回归使用最大似然法估计参数。由于无法找到最大化似然函数的封闭形式解,因此需要使用迭代方法,如随机梯度下降(SGD)。
在 Rust 中使用 `rustlearn` 实现逻辑回归的代码如下:
```rust
pub fn run() -> Result<(), Box<dyn Error>> {
// previous part of the fn ..
let mut model = lr::new(4)
.learning_rate(0.1).l2_penalty(0.5)
.l1_penalty(0.0).one_vs_rest();
for _ in 0..100 { // for 100 epochs
model.fit(&flower_x_train, &flower_y_train).unwrap();
}
let prediction = model.predict(&flower_x_test).unwrap();
let acc1 = accuracy_score(&flower_y_test, &prediction);
println!("Logistic Regression: accuracy: {:?}", acc1);
Ok(())
}
```
运行上述代码可能会得到类似以下的输出:
```plaintext
$ cargo run lr < ../datasets/iris.csv
Logistic Regression: accuracy: 0.36666667
```
逻辑回归的实现流程可以用以下 mermaid 流程图表示:
```mermaid
graph LR
A[初始化模型] --> B[设置超参数]
B --> C[多次迭代训练]
C --> D[进行预测]
D --> E[计算准确率]
```
##### 决策树
在分类问题中,我们的目标是找到一个模型,根据预测变量的值来预测类别变量的值。决策树通过递归地划分特征空间,将其划分为多个不相交的集合,使得每个集合对应一个类别。决策树的构建过程包括两个主要步骤:
1. **生长树**:使用前向选择方法生长一个过大的树,在每一步找到最佳分割点,直到所有终端节点满足以下条件之一:
- 节点中的数据点少于 `n` 个。
- 节点中的所有数据点具有相同的结果(即节点为“纯”节点)。
2. **剪枝**:对生长的树进行剪枝,创建一系列嵌套的树,逐渐降低树的复杂度。
在 Rust 中使用 `rustlearn` 实现决策树的代码如下:
```rust
use rustlearn::trees::decision_tree;
pub fn run() -> Result<(), Box<dyn Error>> {
// data loading and transformations part ...
// similar to the logistic regression secion above ...
let mut decision_tree_model = decision_tree::Hyperparameters::new(
flower_x_train.cols()).one_vs_rest();
decision_tree_model.fit(&flower_x_train, &flower_y_train).unwrap();
let prediction = decision_tree_model.predict(
&flower_x_test).unwrap();
let acc = accuracy_score(
&flower_y_test, &prediction);
println!("DecisionTree model accuracy: {:?}", acc);
Ok(())
}
```
运行上述代码可能会得到类似以下的输出:
```plaintext
$ cargo run trees < ../datasets/iris.csv
Finished dev [unoptimized + debuginfo] target(s) in 0.03s
Running `target/debug/rustlearn_classification_tasks trees`
DecisionTree model accuracy: 0.96666664
```
决策树的构建和预测流程可以用以下表格总结:
| 步骤 | 操作 |
| ---- | ---- |
| 1 | 初始化决策树超参数 |
| 2 | 使用训练数据拟合模型 |
| 3 | 使用测试数据进行预测 |
| 4 | 计算预测准确率 |
##### 随机森林
随机森林是决策树的改进算法,它通过生长多个决策树并进行投票来确定最终的分类结果。每个决策树依赖于一个独立采样的随机向量,并且森林中的所有树具有相同的分布。随着森林中树的数量增加,随机森林的泛化能力会收敛到一个极限。
在 Rust 中使用 `rustlearn` 实现随机森林的代码如下:
```rust
use rustlearn::ensemble::random_forest::Hyperparameters as rf;
pub fn run() -> Result<(), Box<dyn Error>> {
// previous part of the fn ...
let mut tree_params = decision_tree::Hyperparameters::new(
flower_x_train.cols());
tree_params.min_samples_split(10)
.max_features(4);
let mut model = randomforest::new(
tree_params, 10).one_vs_rest();
model.fit(&flower_x_train,
&flower_y_train).unwrap();
let prediction = random_forest_model
.predict(&flower_x_test).unwrap();
let acc = accuracy_score(
&flower_y_test, &prediction);
println!("Random Forest: accuracy: {:?}", acc);
Ok(())
}
```
运行上述代码可能会得到类似以下的输出:
```plaintext
$ cargo run trees < ../datasets/iris.csv
Finished dev [unoptimized + debuginfo] target(s) in 0.03s
Running `target/debug/rustlearn_classification_tasks trees`
DecisionTree model accuracy: 1.0
Random Forest: accuracy: 1.0
```
需要注意的是,在使用简单算法(如逻辑回归)时,准确率可能较低(约 30%),但随着使用基于树的模型(如决策树和随机森林),准确率可以达到 95% - 100%。不过,这些都是玩具数据集,在实际问题中,应谨慎对待如此高的准确率。
随机森林的实现流程可以用以下 mermaid 流程图表示:
```mermaid
graph LR
A[初始化决策树参数] --> B[设置随机森林超参数]
B --> C[训练随机森林模型]
C --> D[进行预测]
D --> E[计算准确率]
```
##### XGBoost
XGBoost(Extreme Gradient Boosting)是一种强大的分类算法,它通过依次构建决策树,使得每棵后续树旨在减少前一棵树的误差。与随机森林不同,XGBoost 使用较浅的树,这些树具有较高的可解释性。在运行 XGBoost 之前,需要设置三种类型的参数:通用参数、提升器参数和任务参数。
在 Rust 中使用 `rust-xgboost` 库实现 XGBoost 的代码如下:
```rust
use xgboost;
use xgboost::{parameters, DMatrix, Booster};
fn read_csv() -> Result<(), Box<dyn Error>> {
// previous data loading and splitting code ...
let mut dtrain = DMatrix::from_dense(&flower_x_train, train_size).unwrap();
dtrain.set_labels(&flower_y_train).unwrap();
let mut dtest = DMatrix::from_dense(&flower_x_test, test_size).unwrap();
dtest.set_labels(&flower_y_test).unwrap();
let lps = parameters::learning::LearningTaskParametersBuilder::default()
.objective(parameters::learning::Objective::MultiSoftmax(3))
.build().unwrap();
let tps = parameters::tree::TreeBoosterParametersBuilder::default()
.max_depth(2).eta(1.0)
.build().unwrap();
let bst_parms = parameters::BoosterParametersBuilder::default()
.booster_type(parameters::BoosterType::Tree(tps))
.learning_params(learning_params)
.verbose(true).build().unwrap();
let ev = &[(&dtrain, "train"), (&dtest, "test")];
let params = parameters::TrainingParametersBuilder::default()
.dtrain(&dtrain).boost_rounds(2)
.booster_params(bst_parms)
.evaluation_sets(Some(ev))
.build().unwrap();
let booster = Booster::train(¶ms).unwrap();
let preds = booster.predict(&dtest).unwrap();
let labels = dtest.get_labels().unwrap();
// find the accuracy
let mut hits = 0;
let mut correct_hits = 0;
for (predicted, actual) in preds.iter().zip(labels.iter()) {
if predicted == actual {
correct_hits += 1;
}
hits += 1;
}
assert_eq!(hits, preds.len());
println!("accuracy={} ({}/{} correct)",
correct_hits as f32 / hits as f32, correct_hits, preds.len());
}
```
运行上述代码可能会得到类似以下的输出:
```plaintext
$ cd chapter2/iris_classification_xgboost
$ cargo run < ../datasets/iris.csv
Finished dev [unoptimized + debuginfo] target(s) in 0.05s
Running `target/debug/iris_classification_xgboost`
[08:26:11] DANGER AHEAD: You have manually specified `updater`
parameter. The `tree_method` parameter will be ignored.
Incorrect sequence of updaters will produce undefined behavior.
For common uses, we recommend using `tree_method` parameter
instead.
[08:26:11] src/tree/updater_prune.cc:74: tree pruning end, 1
roots, 2 extra nodes, 0 pruned nodes, max_depth=1
[08:26:11] src/tree/updater_prune.cc:74: tree pruning end, 1
roots, 4 extra nodes, 0 pruned nodes, max_depth=2
[08:26:11] src/tree/updater_prune.cc:74: tree pruning end, 1
roots, 6 extra nodes, 0 pruned nodes, max_depth=2
[0] test-merror:0 train-merror:0.033333
[08:26:11] src/tree/updater_prune.cc:74: tree pruning end, 1
roots, 2 extra nodes, 0 pruned nodes, max_depth=1
[08:26:11] src/tree/updater_prune.cc:74: tree pruning end, 1
roots, 6 extra nodes, 0 pruned nodes, max_depth=2
[08:26:11] src/tree/updater_prune.cc:74: tree pruning end, 1
roots, 6 extra nodes, 0 pruned nodes, max_depth=2
[1] test-merror:0 train-merror:0.011111
```
XGBoost 的实现步骤可以总结为以下表格:
| 步骤 | 操作 |
| ---- | ---- |
| 1 | 数据预处理,转换为 `DMatrix` 并设置标签 |
| 2 | 设置学习任务参数、树提升器参数和提升器参数 |
| 3 | 设置训练参数,包括训练矩阵、迭代次数等 |
| 4 | 训练提升器 |
| 5 | 进行预测并计算准确率 |
通过以上介绍,我们详细了解了回归模型的评估方法以及多种分类算法在 Rust 中的实现。这些算法各有优缺点,在实际应用中,我们需要根据具体问题选择合适的算法。
0
0
复制全文
相关推荐






