【机器学习】:线性回归与逻辑回归
本人的第二个啃书项目,大概是为了准备中国大学生计算机设计大赛and数学建模。
希望在学习机器学习和深度学习之后能够在竞赛中使用或者创建出更加创新性的模型。
这个项目主要参考
- 《机器学习》——周志华
- 《机器学习公式详解》——谢文睿、秦州
- 《机器学习实战》——Aurelien Geron
代码会写,但语言不一定,可能会使用
线性回归
基本形式
其中,
损失函数
为求得上述损失函数的最值,我们可以得到
更一般的情况是,
这时候构造矩阵:
其中
广义线性模型
比如高中常见的:
下面给出线性回归模型的C++代码(使用了模拟退火以实现随机性,防止过拟合):
展开代码
#pragma once
#include<iostream>
#include<vector>
#include<cmath>
#include<cstdlib>
#include<cstring>
using namespace std;
#define DEBUG
using LL = long long;
// 线性回归器
/// <summary>
/// 线性回归模型
/// 模板参数是自变量的维度
/// </summary>
template<unsigned N>
class LinearRegressor {
// 公有成员
public:
// 构造函数
LinearRegressor() = default;
// 析构函数
~LinearRegressor() = default;
// 训练模型的函数
void fit(
// 自变量与因变量
vector<vector<double>> X, vector<double> y,
// 最大迭代次数
size_t iterations = INT_MAX,
// 最大评估次数
size_t evaluate = 10 * pow(2, N)
) {
// 非法情况
size_t xlen = X.size(), ylen = y.size();
if (xlen != ylen || (xlen == 1 && N == 1)) throw runtime_error("error");
// 获取数据量
size_t len = xlen;
evaluate *= pow(2, len);
// 标记,用于判断迭代是否完成
bool flag = false;
size_t times = 0, idx = 0;
double ans = 0, errorCode = 0x3fffffff;
// 随机数
srand(static_cast<unsigned>(time(NULL)));
// 查询最值
double maxX = 0, minX = 0x3f3f3f3f, maxY = 0, minY = 0x3f3f3f3f;
for (int i = 0; i < len; ++i) {
for (int j = 0; j < N; ++j) {
minX = min(minX, X[i][j]);
maxX = max(maxX, X[i][j]);
}
}
for (int i = 0; i < len; ++i) {
maxY = max(maxY, y[i]);
minY = min(minY, y[i]);
}
// 计算最大虚拟斜率
double myMax = static_cast<double>(maxY - minY) / static_cast<double>(maxX - minX) * 2;
// 随机生成w与b
for (int i = 0; i < N; ++i) {
w[i] = ((rand() / static_cast<double>(RAND_MAX)) - 0.5) * myMax;
}
b = ((rand() / static_cast<double>(RAND_MAX)) - 0.5) * myMax;
// 保存最优结果的数组
double tmpW[N];
double tmpB = 0;
memset(tmpW, 0, sizeof tmpW);
// 开始迭代
while (!flag && times < iterations) {
// 计算当前解的误差
ans = 0;
// 遍历每一个元组
for (int i = 0; i < len; ++i) {
// 求当前预测值
double fy = 0;
// 计算每一个属性
for (int j = 0; j < N; ++j) fy += w[j] * X[i][j];
fy += b;
// 损失函数计算
ans += pow(fy - y[i], 2);
}
// 调试
//#ifdef DEBUG
// cout << times << ":" << endl;
// for (int i = 0; i < N; ++i) {
// cout << "w[" << i << "] = " << w[i] << endl;
// }
// cout << "b = " << b << endl;
// cout << endl;
//#endif // DEBUG
// 判断迭代效果
if (ans < errorCode) {
idx = 0;
errorCode = ans;
#ifdef DEBUG
cout << times << ":" << endl;
for (int i = 0; i < N; ++i) {
cout << "w[" << i << "] = " << w[i] << endl;
}
cout << "b = " << b << endl;
cout << endl;
#endif // DEBUG
// 保存最优结果
for (int i = 0; i < N; ++i) {
tmpW[i] = w[i];
}
tmpB = b;
}
else {
// 连续好几次找不到,说明已经大致收敛
++idx;
if (idx > evaluate) break;
}
if (idx < evaluate) {
// 重新随机w与b
for (int i = 0; i < N; ++i) {
w[i] = ((rand() / static_cast<double>(RAND_MAX)) - 0.5) * myMax;
}
b = ((rand() / static_cast<double>(RAND_MAX)) - 0.5) * myMax;
}
// 迭代次数加一
++times;
}
// 将最优结果保存下来
for (int i = 0; i < N; ++i) w[i] = tmpW[i];
b = tmpB;
}
// 线性回归预测函数
double predict(vector<double> x) {
// 计算结果
double ans = 0;
for (int i = 0; i < N; ++i) {
ans += w[i] * x[i];
}
return ans + b;
}
// 私有成员
private:
// 线性回归模型的参数
double w[N];
double b;
};
逻辑回归
基本介绍
逻辑回归是在线性回归的基础上,结合对数几率函数“
考虑二分类任务,其输出标记为:
更多的,我们使用对数几率函数:
代入
可以将
则有:
损失函数
最大化对数似然
但要注意,逻辑回归也只适合线性数据。
线性判别分析LDA
基本思想
损失函数
使得同类投影点协方差尽可能小,类中心之间的距离尽可能大,即最大化
其中,分母是同类投影点的协方差,分子是类中心之间的距离。
定义类内散度矩阵
在降维过程中可以使用类别的先验知识经验,而像
这样的无监督学习则无法使用类别先验知识。 在样本分类信息依赖均值而不是方差的时候,比 之类的算法较优。
不适合对非高斯分布样本进行降维, 也有这个问题。 降维最多降到类别数 的维数,如果我们降维的维度大于 ,则不能使用 。当然目前有一些 的进化版算法可以绕过这个问题。 在样本分类信息依赖方差而不是均值的时候,降维效果不好。 可能过度拟合数据。
本作品采用 知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议 (CC BY-NC-ND 4.0) 进行许可。