机器学习(1)线性回归

线性回归

首先明确回归任务和分类任务的区别

  • 回归——最终的结果是一个连续的实数
  • 分类——最终的结果是一个离散的值,如二分类:是与不是(1/0)

例如去银行贷款:

能不能贷款,是一个分类任务,输出的是或者不能

能带款多少钱,是一个回归任务,输出的是一个实数值

基本定义

特征:输入的数据

标签:输出的数据

线性回归就是将特征进行线性组合,对标签进行预测,即:
y ^ = θ 0 + θ 1 x 1 + θ 2 x 2 + ⋯ + θ n x n \hat y = \theta_0+\theta_1x_1 + \theta_2x_2 + \dots + \theta_nx_n y^=θ0+θ1x1+θ2x2++θnxn
其中, θ 0 \theta_0 θ0是偏置项

通俗解释就是,找到最合适的一条线(或者想象成一个高维空间)来最好地拟合我们的数据点

在这里插入图片描述

将上述线性回归方程整合,转化为矩阵的形式,得到:
h θ ( x ) = ∑ i = 0 n θ i x i = θ T x h_{\theta}(x) = \sum _{i=0}^{n}\theta_ix_i = \theta^Tx hθ(x)=i=0nθixi=θTx
θ 0 \theta_0 θ0 构造成 θ 0 x 0 \theta_0 x_0 θ0x0,且 x 0 x_0 x0 恒为1

即,特征矩阵(x)可以转化为:
x 0 x 1 … x n 1 x 11 … x 1 n 1 x 21 … x 2 n ⋮ ⋮ ⋮ ⋮ \begin{matrix} x_0 & x_1 & \dots & x_n \\ 1 & x_{11}& \dots & x_{1n} \\ 1 & x_{21}& \dots & x_{2n} \\ \vdots &\vdots &\vdots &\vdots \end{matrix} x011x1x11x21xnx1nx2n

误差项的定义

通过线性回归方程的计算,可以得到一个预测值

为了衡量预测值的准确性,因此要定义误差,即真实值与预测值之间的差异

因此对于每个样本:
e r r ( i ) = y ( i ) − θ T x ( i ) err^{(i)} = y^{(i)} - \theta^Tx^{(i)} err(i)=y(i)θTx(i)
从而,可以得到机器学习的定义:

给定数据data,给定一个目标函数(损失函数),使得我们的模型去学习如何分配一些参数,从而达到目标(损失函数最小)

一般而言,在机器学习中,误差 e r r ( i ) err^{(i)} err(i)是独立并且具有相同的分布,都服从于均值为 0 0 0方差为 θ \theta θ 的高斯分布

将该性质推广到样本,即:

  • 样本之间必须是相互独立的,这是前提

  • 样本要求同分布

  • 高斯分布,即正态分布,大多数样本的数据符合正常情况,极少数的数据较为极端

似然函数

似然函数取得最大值表示相应的参数能够使得统计模型最为合理。

在这里插入图片描述

从而得到似然函数与对数似然

在这里插入图片描述

注:

我们关注的是似然函数的最大值(极大值)所对应的最大值点(极大值),即对应的 θ \theta θ ,而不是似然函数的最大(极大)值

将似然函数展开化简,得:

在这里插入图片描述

因此,我们的目标转化为:让似然函数越大越好(减号后项越小越好)

即,使得下式 J ( θ ) J(\theta) J(θ)最小
J ( θ ) = 1 2 ∑ i = 1 m ( y ( i ) − θ T x ( i ) ) 2 J(\theta) = \frac{1}{2}\sum_{i=1}^{m}(y^{(i)} - \theta^Tx^{(i)})^2 J(θ)=21i=1m(y(i)θTx(i))2

最小二乘法求解参数

公式推导:

J ( θ ) J(\theta) J(θ)展开,为了求得极小值,因此对 θ \theta θ 求偏导,且令偏导为0,最终可以得到
θ = ( X T X ) − 1 X T y \theta = (X^TX)^{-1}X^Ty θ=(XTX)1XTy

在这里插入图片描述

关键问题

X T X X^TX XTX在很多条件下是不可逆得,因此不适用最小二乘法

梯度下降

通过计算损失函数 l o s s loss loss的梯度,沿着其反方向,找到 l o s s loss loss降低最快的方向,进行参数更新

相当于下山问题:每次沿着坡度最大的方向进行下山,可以在最短时间内到达山谷最低点

基本步骤:

  • 找到合适的方向,即每次沿着梯度的反方向更新
  • 选择合适的步长(学习率),不宜过大也不宜过小
  • 按照方向与步伐去更新我们的参数(对每个参数分别进行更新
批量梯度下降

每次对更新参数时考虑所有样本,容易得到最优解,但速度较慢
∂ J ( θ ) ∂ θ j = − 1 m ∑ i = 1 m ( y i − h θ ( x i ) ) x j i \frac{\partial J(\theta)}{\partial \theta_j} = -\frac{1}{m}\sum_{i=1}^{m}(y^i - h_{\theta}(x^i))x_j^i θjJ(θ)=m1i=1m(yihθ(xi))xji

θ j = θ j + α 1 m ∑ i = 1 m ( y i − h θ ( x i ) ) x j i \theta _j = \theta_j + \alpha\frac{1}{m}\sum_{i=1}^{m}(y^i - h_{\theta}(x^i))x_j^i θj=θj+αm1i=1m(yihθ(xi))xji

随机梯度下降

每次随机找一个样本,进行更新参数,迭代速度较快,单不一定每次都朝着收敛的方向
θ j = θ j + α ( y i − h θ ( x i ) ) x j i \theta _j = \theta_j + \alpha(y^i - h_{\theta}(x^i))x_j^i θj=θj+α(yihθ(xi))xji

小批量梯度下降

随机梯度下降与批量梯度下降的综合,每次更新拿一小部分数据( b a t c h batch batch)来算,比较实用
θ j = θ j + α 1 b a t c h ∑ k = i i + b a t c h ( y ( k ) − h θ ( x ( k ) ) ) x j ( k ) \theta _j = \theta_j + \alpha\frac{1}{batch}\sum_{k=i}^{i+batch}(y^{(k)} - h_{\theta}(x^{(k)}))x_j^{(k)} θj=θj+αbatch1k=ii+batch(y(k)hθ(x(k)))xj(k)
超参数的选择:

  • b a t c h batch batch一般选择64、128、256…
  • 学习率 α \alpha α 一般选择 0.01、0.001
代码实现

整体流程

  • 数据预处理——归一化、标准化
  • 利用梯度下降算法迭代训练模型,求解参数值
    • 定义 l o s s loss loss
  • 预测模型

线性回归代码LinearRegression.py

import numpy as np
from utils.features import prepare_for_training

class LinearRegression:
    '''
    线性回归模型
    '''
    def __init__(self, data, labels, polynomial_degress=0,sinusoid_degree=0, normalize_data=True):
        '''
        :param data:  数据
        :param labels:  标签
        :param polynomial_degress: 是否要对数据进行一些额外变换
        :param sinusoid_degree:  数据的非线性变换
        :param normalize_data:  是否进行初始化 默认为是
        '''
        ## 数据预处理
        (data_processed, features_mean, features_deviation) = prepare_for_training(data,
                                                                                   polynomial_degress,
                                                                                   sinusoid_degree)
        self.data = data_processed
        self.labels = labels
        self.features_mean = features_mean
        self.features_deviation = features_deviation
        self.polynomial_degress = polynomial_degress
        self.sinusoid_degree = sinusoid_degree
        self.normalize_data = normalize_data

        ## 构造参数
        num_features = self.data.shape[1]
        self.theta = np.zeros((num_features, 1))  # 列向量
    def train(self, alpha, num_iterations = 500):
        '''
        模型训练——num_iterations次梯度下降
        :param alpha: 学习率
        :param num_iterations: 迭代次数
        :return:
        '''
        cost_history = self.gradient_descent(alpha, num_iterations)
        return self.theta, cost_history

    def gradient_descent(self, alpha, num_iterations):
        '''
        梯度下降
        :param alpha: 学习率
        :param num_iterations: 迭代次数
        :return: 每一步的损失
        '''
        cost_history = []  #记录每一步的损失
        for _ in range(num_iterations):
            self.gradient_step(alpha)
            cost_history.append(self.cost_function(self.data, self.labels))
        return cost_history
    def gradient_step(self, alpha):
        '''
        每一步梯度下降的计算
        :param alpha: 学习率
        :return:
        '''
        num_examples = self.data.shape[0]
        prediction = LinearRegression.hypothesis(self.data, self.theta)
        delta = prediction - self.labels
        # 注意要把 delta 转置后在与self.data做dot
        new_theta = self.theta - alpha*(1 / num_examples)*(np.dot(delta.T, self.data)).T
        self.theta = new_theta #更新参数
    @staticmethod  # 定义成静态 不会传递self数据
    def hypothesis(data, theta):
        '''
        预测函数
        :param theta: 参数
        :return: 预测值
        '''
        prediction = np.dot(data, theta)
        return prediction
    def cost_function(self, data, labels):
        num_examples = data.shape[0]
        delta = LinearRegression.hypothesis(data, self.theta) - labels
        cost = (1/2)*np.dot(delta.T, delta)  # 矩阵的平方
        print(cost.shape)
        return cost[0][0]  # ???
    def get_cost(self, data, labels):
        '''
        获得误差 用于测试阶段
        要先对data进行预处理
        '''
        data_processed= prepare_for_training(data,
                                            self.polynomial_degress,
                                            self.sinusoid_degree)[0] #只需要获得data值 因此加上[0]
        return self.cost_function(data_processed, labels)
    def predict(self, data):
        '''
        预测 用于测试阶段 用训练好的模型得到回归结果
        要先对data进行预处理
        '''
        data_processed = prepare_for_training(data,
                                              self.polynomial_degress,
                                              self.sinusoid_degree)[0]  # 只需要获得data值 因此加上[0]
        return self.hypothesis(data_processed, self.theta)

测试部分 test.py

import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
from LinearRegression import LinearRegression
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False

########## 数据读取与处理 ##########
data = pd.read_csv('./data/world-happiness-report-2017.csv')
## 划分训练测试集
train_data = data.sample(frac=0.8)  # 指定训练集占80%
test_data = data.drop(train_data.index)

input_param_name = 'Economy..GDP.per.Capita.'
output_param_name = 'Happiness.Score'

# 注意
x_train =  train_data[[input_param_name]].values
y_train =  train_data[[output_param_name]].values
x_test = test_data[[input_param_name]].values
y_test = test_data[[output_param_name]].values

## 数据展示
plt.scatter(x_train, y_train, label='Train_data')
plt.scatter(x_test, y_test, label='Test_data')
plt.xlabel(input_param_name)
plt.ylabel(output_param_name)
plt.legend()
plt.show()

########## 模型训练 ##########
## 参数初始化
num_iterations = 500
learning_rate = 0.01

linear_model = LinearRegression(x_train, y_train)
[theta, cost_history] = linear_model.train(learning_rate, num_iterations)
print('开始时的损失:', cost_history[0])
print('训练后的损失', cost_history[-1])

plt.plot(range(num_iterations), cost_history)
plt.xlabel('迭代次数')
plt.ylabel('损失loss')
plt.show()

########## 模型测试 ##########
y_prediction = linear_model.predict(x_test)
## 结果展示
plt.scatter(x_train, y_train, label='Train_data')
plt.scatter(x_test, y_test, label='Test_data')
plt.plot(x_test, y_prediction, 'r', label='Prediction')
plt.xlabel(input_param_name)
plt.ylabel(output_param_name)
plt.legend()
plt.show()

预测结果

在这里插入图片描述

在这里插入图片描述

  • 23
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值