MK
摩柯社区 - 一个极简的技术知识社区
AI 面试

Ruby与机器学习的简单集成方法

2024-07-043.6k 阅读

Ruby 基础回顾

在深入探讨 Ruby 与机器学习的集成之前,我们先来简单回顾一下 Ruby 的一些基础特性。Ruby 是一种面向对象、动态类型的编程语言,以其简洁、灵活和优雅的语法著称。

数据类型与变量

Ruby 中的主要数据类型包括字符串(String)、数字(Numeric,包含整数 Integer 和浮点数 Float 等)、数组(Array)、哈希(Hash)等。例如,定义一个字符串变量:

name = "John"
puts name

定义一个数组变量:

numbers = [1, 2, 3, 4, 5]
puts numbers

哈希则是一种键值对的数据结构,如下所示:

person = {name: "Jane", age: 30}
puts person[:name]
puts person[:age]

控制结构

Ruby 提供了常见的控制结构,如 if - else 语句用于条件判断:

number = 10
if number > 5
  puts "数字大于 5"
else
  puts "数字小于等于 5"
end

循环结构有 for 循环和 while 循环。for 循环示例:

for i in 1..5
  puts i
end

while 循环示例:

count = 1
while count <= 5
  puts count
  count += 1
end

函数定义

在 Ruby 中定义函数非常简单,例如:

def add_numbers(a, b)
  return a + b
end

result = add_numbers(3, 5)
puts result

这里定义了一个 add_numbers 函数,接受两个参数并返回它们的和。

机器学习基础概念

在将 Ruby 与机器学习集成之前,了解一些基本的机器学习概念是必要的。

机器学习类型

  1. 监督学习:在监督学习中,我们有一组带有标签(目标变量)的训练数据。算法的目标是学习从输入特征到标签的映射关系,以便对新的、未见过的数据进行预测。例如,根据房屋的面积、卧室数量等特征预测房价,这就是一个典型的监督学习任务,具体又分为回归(预测连续值,如房价)和分类(预测离散类别,如判断一封邮件是否为垃圾邮件)。
  2. 无监督学习:无监督学习处理的是没有标签的数据。算法的目标是在数据中发现模式或结构,比如聚类算法将相似的数据点划分到同一个簇中,降维算法则试图在保留数据主要信息的同时减少数据的维度。
  3. 强化学习:强化学习关注的是智能体(agent)如何在环境中采取一系列行动,以最大化累积奖励。智能体通过与环境进行交互,根据环境反馈的奖励信号来学习最优策略。例如,机器人在迷宫中寻找出口,每走一步会得到不同的奖励,机器人需要学习如何行动以获得最大奖励从而走出迷宫。

常见机器学习算法

  1. 线性回归:线性回归是一种用于预测连续值的监督学习算法。它假设目标变量与输入特征之间存在线性关系。数学上,简单线性回归模型可以表示为 (y = \beta_0 + \beta_1x_1+\epsilon),其中 (y) 是目标变量,(x_1) 是输入特征,(\beta_0) 是截距,(\beta_1) 是系数,(\epsilon) 是误差项。在多元线性回归中,有多个输入特征 (x_1,x_2,\cdots,x_n)。
  2. 逻辑回归:尽管名字中有“回归”,但逻辑回归实际上是一种分类算法。它用于预测离散的类别,通常是二分类(0 或 1)。逻辑回归使用逻辑函数(sigmoid 函数)将线性组合的输入特征映射到 0 到 1 之间的概率值,然后通过设定阈值(通常为 0.5)来进行类别预测。
  3. 决策树:决策树是一种树形结构的分类或回归算法。每个内部节点表示一个特征上的测试,分支表示测试输出,叶节点表示类别或数值预测结果。决策树通过递归地划分数据空间来构建,使得每个叶节点中的数据尽可能属于同一类别(分类树)或具有相似的值(回归树)。
  4. 支持向量机(SVM):SVM 是一种强大的二分类算法,旨在找到一个最优超平面,将不同类别的数据点分开,并且使两类数据点到超平面的距离最大化。对于非线性可分的数据,SVM 可以通过核函数将数据映射到高维空间,从而找到合适的超平面。

Ruby 与机器学习集成的工具

Ruby 中的机器学习库

  1. Ruby - scikit - learn:虽然 scikit - learn 主要是 Python 的机器学习库,但通过一些工具可以在 Ruby 中使用它。例如,可以通过 ruby - scikit - learn 库。它允许 Ruby 开发者利用 scikit - learn 丰富的机器学习算法和工具。安装方式通常为:
gem install ruby - scikit - learn
  1. TorchRuby:TorchRuby 是 Ruby 对 PyTorch 的绑定。PyTorch 是一个广泛使用的深度学习框架,TorchRuby 让 Ruby 开发者能够使用 PyTorch 的张量操作、神经网络构建等功能来进行深度学习相关的机器学习任务。安装 TorchRuby 时,需要先安装 Ruby 以及一些必要的依赖库,然后通过 gem install torch - ruby 进行安装。
  2. DL4Ruby:DL4Ruby 是一个专门为 Ruby 设计的深度学习框架。它提供了一系列用于构建和训练神经网络的工具和接口,支持多种神经网络架构,如多层感知机(MLP)、卷积神经网络(CNN)和循环神经网络(RNN)等。安装 DL4Ruby 可以使用 gem install dl4ruby

数据处理库

  1. Narray:Narray 是 Ruby 中的数值计算库,类似于 Python 的 NumPy。它提供了高效的多维数组操作,对于机器学习中的数据预处理、特征工程等环节非常有用。例如,在处理大规模数据集时,Narray 可以大大提高计算效率。安装 Narray 可以使用 gem install narray
  2. DataFramesRuby:DataFramesRuby 提供了类似 Python 的 Pandas DataFrame 的数据结构和操作方法。它使得数据的读取、清洗、转换和分析变得更加方便。例如,可以轻松地读取 CSV 文件并进行数据筛选、聚合等操作。安装 DataFramesRuby 可以使用 gem install dataframes - ruby

使用 Ruby 进行简单的机器学习任务

线性回归示例

我们以一个简单的线性回归任务为例,使用 ruby - scikit - learn 库。假设我们有一组关于房屋面积和价格的数据,我们希望通过房屋面积来预测价格。

首先,安装 ruby - scikit - learn 库:

gem install ruby - scikit - learn

然后编写代码:

require 'ruby - scikit - learn'
require 'numpy'

# 房屋面积数据(平方米)
X = Numpy.array([[100], [120], [150], [80], [90]])
# 对应的房价数据(万元)
y = Numpy.array([200, 250, 300, 160, 180])

# 创建线性回归模型
model = ScikitLearn::LinearRegression.new

# 训练模型
model.fit(X, y)

# 预测新的房屋价格
new_area = Numpy.array([[130]])
predicted_price = model.predict(new_area)

puts "预测的房屋价格为:#{predicted_price[0]} 万元"

在这段代码中,我们首先引入了 ruby - scikit - learnnumpy 库。然后定义了房屋面积和价格的数据。接着创建了线性回归模型,并使用数据进行训练。最后,我们输入一个新的房屋面积,预测其对应的价格。

分类示例(逻辑回归)

假设我们有一个简单的二分类任务,判断一个人是否患有某种疾病,特征是年龄和血压。

安装 ruby - scikit - learn 库后,编写如下代码:

require 'ruby - scikit - learn'
require 'numpy'

# 特征数据:年龄和血压
X = Numpy.array([
  [30, 120],
  [40, 130],
  [50, 140],
  [25, 110],
  [35, 125]
])

# 标签数据:0 表示未患病,1 表示患病
y = Numpy.array([0, 1, 1, 0, 1])

# 创建逻辑回归模型
model = ScikitLearn::LogisticRegression.new

# 训练模型
model.fit(X, y)

# 预测新的数据
new_data = Numpy.array([[38, 132]])
predicted_class = model.predict(new_data)

puts "预测的类别为:#{predicted_class[0]}"

这里我们创建了逻辑回归模型,使用给定的年龄和血压数据进行训练,然后对新的数据进行类别预测。

聚类示例(K - Means 聚类)

假设我们有一组二维数据点,我们希望使用 K - Means 聚类算法将它们划分成不同的簇。

安装 ruby - scikit - learn 库后,代码如下:

require 'ruby - scikit - learn'
require 'numpy'

# 生成一些随机的二维数据点
X = Numpy.random.randn(100, 2)

# 创建 K - Means 聚类模型,设置簇的数量为 3
model = ScikitLearn::KMeans.new(n_clusters: 3)

# 进行聚类
model.fit(X)

# 获取每个数据点的簇标签
labels = model.labels_

puts "每个数据点的簇标签:"
puts labels

在这段代码中,我们首先生成了一些随机的二维数据点。然后创建了 K - Means 聚类模型,并指定簇的数量为 3。接着对数据进行聚类,并获取每个数据点所属的簇标签。

利用 DL4Ruby 进行深度学习

构建简单的神经网络

DL4Ruby 提供了方便的接口来构建神经网络。我们以构建一个简单的多层感知机(MLP)为例,用于手写数字识别(假设我们有相应的数据集)。

首先安装 DL4Ruby:

gem install dl4ruby

然后编写代码:

require 'dl4ruby'

# 创建一个简单的多层感知机
mlp = DL::Model.new do
  input [784] # 手写数字图像是 28x28 = 784 维向量
  fully_connected 128, activation: :relu
  fully_connected 10, activation: :softmax
end

# 定义损失函数和优化器
criterion = DL::Loss::CrossEntropy.new
optimizer = DL::Optimizer::SGD.new(mlp.parameters, lr: 0.01)

# 假设我们有训练数据 X 和标签 y
# 这里只是示例,实际需要加载真实数据
X = DL::Tensor.randn(100, 784)
y = DL::Tensor.randint(0, 9, [100])

# 训练模型
100.times do |epoch|
  output = mlp.forward(X)
  loss = criterion.forward(output, y)
  mlp.zero_grad
  criterion.backward
  optimizer.step
  puts "Epoch #{epoch + 1}, Loss: #{loss.to_f}"
end

在这段代码中,我们首先创建了一个多层感知机,它有一个输入层(784 维,对应手写数字图像的像素数),一个隐藏层(128 个神经元,使用 ReLU 激活函数)和一个输出层(10 个神经元,对应 10 个数字类别,使用 softmax 激活函数)。然后定义了交叉熵损失函数和随机梯度下降优化器。接着我们生成了一些随机的训练数据(实际应用中需要加载真实的手写数字数据集),并进行了 100 次训练迭代,每次迭代计算损失并更新模型参数。

卷积神经网络(CNN)示例

对于图像识别等任务,卷积神经网络(CNN)表现出色。下面我们使用 DL4Ruby 构建一个简单的 CNN 用于图像分类。

安装 DL4Ruby 后,代码如下:

require 'dl4ruby'

# 创建一个简单的卷积神经网络
cnn = DL::Model.new do
  input [3, 32, 32] # 假设输入图像是 3 通道,32x32 大小
  conv2d 16, kernel_size: [3, 3], padding: [1, 1], activation: :relu
  max_pool2d kernel_size: [2, 2], stride: [2, 2]
  conv2d 32, kernel_size: [3, 3], padding: [1, 1], activation: :relu
  max_pool2d kernel_size: [2, 2], stride: [2, 2]
  flatten
  fully_connected 128, activation: :relu
  fully_connected 10, activation: :softmax
end

# 定义损失函数和优化器
criterion = DL::Loss::CrossEntropy.new
optimizer = DL::Optimizer::Adam.new(cnn.parameters, lr: 0.001)

# 假设我们有训练数据 X 和标签 y
# 这里只是示例,实际需要加载真实数据
X = DL::Tensor.randn(100, 3, 32, 32)
y = DL::Tensor.randint(0, 9, [100])

# 训练模型
50.times do |epoch|
  output = cnn.forward(X)
  loss = criterion.forward(output, y)
  cnn.zero_grad
  criterion.backward
  optimizer.step
  puts "Epoch #{epoch + 1}, Loss: #{loss.to_f}"
end

在这个 CNN 模型中,我们首先定义了输入层,假设输入图像是 3 通道、32x32 大小。然后依次添加了卷积层、池化层、全连接层等。同样定义了交叉熵损失函数和 Adam 优化器,并使用随机生成的数据进行了 50 次训练迭代。

模型评估与调优

模型评估指标

  1. 回归任务
    • 均方误差(MSE):MSE 衡量的是预测值与真实值之间误差的平方的平均值。数学公式为 (MSE=\frac{1}{n}\sum_{i = 1}^{n}(y_i-\hat{y}_i)^2),其中 (y_i) 是真实值,(\hat{y}_i) 是预测值,(n) 是样本数量。在 Ruby 中,使用 ruby - scikit - learn 库计算 MSE 的示例代码如下:
require 'ruby - scikit - learn'
require 'numpy'

# 真实值
y_true = Numpy.array([1, 2, 3, 4, 5])
# 预测值
y_pred = Numpy.array([1.2, 2.1, 2.9, 4.2, 4.8])

mse = ScikitLearn::Metrics.mean_squared_error(y_true, y_pred)
puts "均方误差:#{mse}"
- **平均绝对误差(MAE)**:MAE 是预测值与真实值之间误差的绝对值的平均值,公式为 \(MAE=\frac{1}{n}\sum_{i = 1}^{n}|y_i-\hat{y}_i|\)。在 Ruby 中计算 MAE 的代码如下:
require 'ruby - scikit - learn'
require 'numpy'

y_true = Numpy.array([1, 2, 3, 4, 5])
y_pred = Numpy.array([1.2, 2.1, 2.9, 4.2, 4.8])

mae = ScikitLearn::Metrics.mean_absolute_error(y_true, y_pred)
puts "平均绝对误差:#{mae}"
  1. 分类任务
    • 准确率(Accuracy):准确率是分类正确的样本数占总样本数的比例,公式为 (Accuracy=\frac{TP + TN}{TP + TN+FP+FN}),其中 (TP) 是真正例(实际为正类且预测为正类),(TN) 是真负例(实际为负类且预测为负类),(FP) 是假正例(实际为负类但预测为正类),(FN) 是假负例(实际为正类但预测为负类)。在 Ruby 中计算准确率的示例代码如下:
require 'ruby - scikit - learn'
require 'numpy'

y_true = Numpy.array([0, 1, 0, 1])
y_pred = Numpy.array([0, 1, 0, 1])

accuracy = ScikitLearn::Metrics.accuracy_score(y_true, y_pred)
puts "准确率:#{accuracy}"
- **精确率(Precision)**:精确率是真正例占预测为正例的比例,公式为 \(Precision=\frac{TP}{TP + FP}\)。在 Ruby 中计算精确率的代码如下:
require 'ruby - scikit - learn'
require 'numpy'

y_true = Numpy.array([0, 1, 0, 1])
y_pred = Numpy.array([0, 1, 1, 1])

precision = ScikitLearn::Metrics.precision_score(y_true, y_pred)
puts "精确率:#{precision}"
- **召回率(Recall)**:召回率是真正例占实际为正例的比例,公式为 \(Recall=\frac{TP}{TP + FN}\)。在 Ruby 中计算召回率的代码如下:
require 'ruby - scikit - learn'
require 'numpy'

y_true = Numpy.array([0, 1, 0, 1])
y_pred = Numpy.array([0, 1, 1, 1])

recall = ScikitLearn::Metrics.recall_score(y_true, y_pred)
puts "召回率:#{recall}"

模型调优方法

  1. 交叉验证:交叉验证是一种评估模型性能并选择最佳超参数的方法。常见的交叉验证方法有 K - 折交叉验证。在 K - 折交叉验证中,将数据集分成 K 个互不重叠的子集,每次使用其中 K - 1 个子集作为训练集,剩余的 1 个子集作为验证集。重复 K 次,使得每个子集都作为验证集一次。在 Ruby 中使用 ruby - scikit - learn 进行 K - 折交叉验证的示例代码如下:
require 'ruby - scikit - learn'
require 'numpy'

# 假设我们有特征数据 X 和标签数据 y
X = Numpy.array([[1], [2], [3], [4], [5]])
y = Numpy.array([0, 1, 0, 1, 0])

# 创建线性回归模型
model = ScikitLearn::LinearRegression.new

# 进行 5 折交叉验证
cv = ScikitLearn::ModelSelection::KFold.new(n_splits: 5)
cv.each do |train_index, test_index|
  X_train, X_test = X[train_index], X[test_index]
  y_train, y_test = y[train_index], y[test_index]
  model.fit(X_train, y_train)
  y_pred = model.predict(X_test)
  mse = ScikitLearn::Metrics.mean_squared_error(y_test, y_pred)
  puts "MSE for this fold: #{mse}"
end
  1. 超参数调优:超参数是在模型训练之前需要设置的参数,不同的超参数值可能会导致模型性能的显著差异。常见的超参数调优方法有网格搜索(Grid Search)和随机搜索(Random Search)。
    • 网格搜索:网格搜索是一种穷举法,它在给定的超参数取值范围内,尝试所有可能的超参数组合,然后根据评估指标选择最优的超参数组合。在 Ruby 中使用 ruby - scikit - learn 进行网格搜索的示例代码如下:
require 'ruby - scikit - learn'
require 'numpy'

# 假设我们有特征数据 X 和标签数据 y
X = Numpy.array([[1], [2], [3], [4], [5]])
y = Numpy.array([0, 1, 0, 1, 0])

# 创建逻辑回归模型
model = ScikitLearn::LogisticRegression.new

# 定义超参数搜索空间
param_grid = {
  C: [0.1, 1, 10],
  penalty: ['l1', 'l2']
}

# 创建网格搜索对象
grid_search = ScikitLearn::ModelSelection::GridSearchCV.new(model, param_grid, cv: 5)

# 进行网格搜索
grid_search.fit(X, y)

# 输出最佳超参数和最佳得分
puts "Best parameters: #{grid_search.best_params_}"
puts "Best score: #{grid_search.best_score_}"
- **随机搜索**:随机搜索与网格搜索类似,但它不是尝试所有可能的超参数组合,而是在超参数取值范围内随机选择一定数量的组合进行评估。这样可以在较短时间内探索较大的超参数空间。在 Ruby 中使用 `ruby - scikit - learn` 进行随机搜索的示例代码如下:
require 'ruby - scikit - learn'
require 'numpy'

# 假设我们有特征数据 X 和标签数据 y
X = Numpy.array([[1], [2], [3], [4], [5]])
y = Numpy.array([0, 1, 0, 1, 0])

# 创建逻辑回归模型
model = ScikitLearn::LogisticRegression.new

# 定义超参数搜索空间
param_dist = {
  C: ScikitLearn::ModelSelection::RandomizedSearchCV::loguniform(0.1, 10),
  penalty: ['l1', 'l2']
}

# 创建随机搜索对象
random_search = ScikitLearn::ModelSelection::RandomizedSearchCV.new(model, param_dist, n_iter: 3, cv: 5)

# 进行随机搜索
random_search.fit(X, y)

# 输出最佳超参数和最佳得分
puts "Best parameters: #{random_search.best_params_}"
puts "Best score: #{random_search.best_score_}"

通过以上方法,我们可以更好地评估和优化使用 Ruby 构建的机器学习模型,提高模型的性能和泛化能力。在实际应用中,根据具体问题选择合适的评估指标和调优方法是非常重要的。