WA_automat

【机器学习】:线性回归与逻辑回归

N 人看过

本人的第二个啃书项目,大概是为了准备中国大学生计算机设计大赛and数学建模。

希望在学习机器学习和深度学习之后能够在竞赛中使用或者创建出更加创新性的模型。

这个项目主要参考

  1. 《机器学习》——周志华
  2. 《机器学习公式详解》——谢文睿、秦州
  3. 《机器学习实战》——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

基本思想

的思想非常朴素,给定训练样例集,设法将样例投影到一条直线上,使得同类样例的投影点尽可能接近、异类样例点尽可能远离;在对新样本进行分类时,将其投影到同样的这条直线上,再根据投影点的位置来确定样本的类别。

损失函数

使得同类投影点协方差尽可能小,类中心之间的距离尽可能大,即最大化

其中,分母是同类投影点的协方差,分子是类中心之间的距离。

定义类内散度矩阵与类间散度矩阵

算法既可以用来降维,又可以用来分类,但是目前来说,主要还是用于降维。在我们进行图像识别图像识别相关的数据分析时,LDA是一个有力的工具。下面总结下算法的优缺点。

算法的主要优点有:

  1. 在降维过程中可以使用类别的先验知识经验,而像这样的无监督学习则无法使用类别先验知识。

  2. 在样本分类信息依赖均值而不是方差的时候,比之类的算法较优。

算法的主要缺点有:

  1. 不适合对非高斯分布样本进行降维,也有这个问题。

  2. 降维最多降到类别数的维数,如果我们降维的维度大于,则不能使用。当然目前有一些的进化版算法可以绕过这个问题。

  3. 在样本分类信息依赖方差而不是均值的时候,降维效果不好。

  4. 可能过度拟合数据。

本作品采用 知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议 (CC BY-NC-ND 4.0) 进行许可。