机器学习:线性回归 代码+测试

朴素线性回归的实现,直接通过梯度为0解出w的估计值

import numpy as np

class OLSLinearRegression:

    def _ols(self, X, y):
        '''最小二乘法估算w'''
#         tmp = np.linalg.inv(np.matmul(X.T, X))
#         tmp = np.matmul(tmp, X.T)
#         return np.matmul(tmp, y)

        # 若使用较新的python和numpy版本, 可使用如下实现.
        return np.linalg.inv(X.T @ X) @ X.T @ y

    def _preprocess_data_X(self, X):
        '''数据预处理'''

        # 扩展X, 添加x0列并置1.
        m, n = X.shape
        X_ = np.empty((m, n + 1))
        X_[:, 0] = 1
        X_[:, 1:] = X

        return X_

    def train(self, X_train, y_train):
        '''训练模型'''

        # 预处理X_train(添加x0=1)
        X_train = self._preprocess_data_X(X_train)  

        # 使用最小二乘法估算w
        self.w = self._ols(X_train, y_train)

    def predict(self, X):
        '''预测'''
        # 预处理X_train(添加x0=1)
        X = self._preprocess_data_X(X)  
        return np.matmul(X, self.w)

一般梯度下降算法拟合参数

import numpy as np

class GDLinearRegression:

    def __init__(self, n_iter=200, eta=1e-3, tol=None):
        # 训练迭代次数
        self.n_iter = n_iter
        # 学习率
        self.eta = eta
        # 误差变化阈值
        self.tol = tol
        # 模型参数w(训练时初始化)
        self.w = None

    def _loss(self, y, y_pred):
        '''计算损失'''
        return np.sum((y_pred - y) ** 2) / y.size

    def _gradient(self, X, y, y_pred):
        '''计算梯度'''
        return np.matmul(y_pred - y, X) / y.size

    def _gradient_descent(self, w, X, y):
        '''梯度下降算法'''

        # 若用户指定tol, 则启用早期停止法.
        if self.tol is not None:
            loss_old = np.inf

        # 使用梯度下降至多迭代n_iter次, 更新w.
        for step_i in range(self.n_iter):
            # 预测
            y_pred = self._predict(X, w)
            # 计算损失
            loss = self._loss(y, y_pred)
            print('%4i Loss: %s' % (step_i, loss))

            # 早期停止法
            if self.tol is not None:
                # 如果损失下降不足阈值, 则终止迭代.
                if loss_old - loss < self.tol:
                    break
                loss_old = loss

            # 计算梯度
            grad = self._gradient(X, y, y_pred)
            # 更新参数w
            w -= self.eta * grad

    def _preprocess_data_X(self, X):
        '''数据预处理'''

        # 扩展X, 添加x0列并置1.
        m, n = X.shape
        X_ = np.empty((m, n + 1))
        X_[:, 0] = 1
        X_[:, 1:] = X

        return X_

    def train(self, X_train, y_train):
        '''训练'''

        # 预处理X_train(添加x0=1)
        X_train = self._preprocess_data_X(X_train)  

        # 初始化参数向量w
        _, n = X_train.shape
        self.w = np.random.random(n) * 0.05

        # 执行梯度下降训练w
        self._gradient_descent(self.w, X_train, y_train)

    def _predict(self, X, w):
        '''预测内部接口, 实现函数h(x).'''
        return np.matmul(X, w)

    def predict(self, X):
        '''预测'''
        X = self._preprocess_data_X(X)  
        return self._predict(X, self.w)

使用随机梯度下降的算法拟合参数:

import numpy as np

class SGDLinearRegression:

    def __init__(self, n_iter=1000, eta=0.01):
        # 训练迭代次数
        self.n_iter = n_iter
        # 学习率
        self.eta = eta
        # 模型参数theta (训练时根据数据特征数量初始化)
        self.theta = None

    def _gradient(self, xi, yi, theta):
        # 计算当前梯度
        return -xi * (yi - np.dot(xi, theta))

    def _stochastic_gradient_descent(self, X, y, eta, n_iter):
        # 复制X(避免随机乱序时改变原X)
        X = X.copy()
        m, _ = X.shape

        eta0 = eta
        step_i = 0

        for j in range(n_iter):
            # 随机乱序
            np.random.shuffle(X)
            for i in range(m):
                # 计算梯度(每次只使用其中一个样本)
                grad = self._gradient(X[i], y[i], self.theta)
                # 更新参数theta
                step_i += 1
                #eta = 4.0 / (1.0 + i + j) + 0.000000001
                eta = eta0 / np.power(step_i, 0.25)
                self.theta += eta * -grad

    def train(self, X, y):
        if self.theta is None:
            _, n = X.shape
            self.theta = np.zeros(n)

        self._stochastic_gradient_descent(X, y, self.eta, self.n_iter)

    def predict(self, X):
        return np.dot(X, self.theta)

测试代码:

数据集的下载网址:

http://archive.ics.uci.edu/ml/machine-learning-databases/wine-quality/

一、加载数据与数据展示:

import numpy as np
data=np.genfromtxt(r'F:\python_test\data\winequality-red.csv',delimiter=';',skip_header=True)
X=data[:,:-1]
print(X)
#输出X展平之后的大小
print(X.size)
#输出X的形状
print(X.shape)
#结果集y
y=data[:,-1]
print(y)
print(y.size)

结果:

 二、划分测试集与训练集

from sklearn.model_selection import train_test_split
X_train,X_test,y_train,y_test=train_test_split(X,y,test_size=0.3)
from keras.losses import mean_squared_error
from sklearn.metrics import mean_absolute_error

三、使用朴素线性回归进行拟合

ols_lr=OLSLinearRegression()

ols_lr.train(X_train,y_train)
y_pred=ols_lr.predict(X_test)
print('=========================朴素线性回归========================')
print('在测试集上的预测结果:',y_pred)
mse=mean_squared_error(y_test,y_pred)
print('在30%的测试集上的均方误差mse:',mse)
y_train_pred=ols_lr.predict(X_train)
mse_train=mean_squared_error(y_train,y_train_pred)
print('在70%地训练集上的均方误差mse:',mse_train)
#计算平均绝对误差
mae=mean_absolute_error(y_test,y_pred)
print('在30%的测试集上的平均绝对误差mae:',mae)

结果:

 四、使用梯度下降算法拟合参数

gd_lr=GDLinearRegression(n_iter=3000,eta=0.0001,tol=0.00001)
gd_lr.train(X_train,y_train)

print(X.mean(axis=0))#观察每个属性的均值,发现相差过大,所以需要标准化处理

结果:

 注意到损失函数并未收敛,并且属性的均值相差过大,需要进行标准化。学习率需要手动调整,防止震荡而无法收敛

五、使用标准化数据再次测试,在这里只是用训练集计算标准差以及均值

from sklearn.preprocessing import StandardScaler
ss=StandardScaler()
ss.fit(X_train)

结果:

 查看训练集与测试集:

X_train_std=ss.transform(X_train)
X_test_std=ss.transform(X_test)
X_train_std[:3]

X_test_std

 重新拟合参数:

gd_lr=GDLinearRegression(n_iter=3000,eta=0.05,tol=0.00001)
gd_lr.train(X_train_std,y_train)

 可见137次之后便以及收敛到0.43,loss函数的值和之前朴素线性回归的值已经很接近

六、对测试集进行预测并且计算误差:

均方误差和OLS相比已经差不多了,平均绝对误差也是差不多了

y_pred=gd_lr.predict(X_test_std)
mse=mean_squared_error(y_test,y_pred)

mae=mean_absolute_error(y_test,y_pred)
mae

 七、使用随机梯度下降算法进行计算

可见情况没有上述两种好

猜你喜欢

转载自www.cnblogs.com/randy-lo/p/13209771.html
今日推荐