首页 > 其他分享 >机器学习笔记(一)从波士顿房价预测开始,梯度下降

机器学习笔记(一)从波士顿房价预测开始,梯度下降

时间:2023-12-22 14:25:27浏览次数:33  
标签:gradient 梯度 self 笔记 num np 波士顿 data def

从波士顿房价开始

目标

其实这一章节比较简单,主要是概念,首先在波士顿房价这个问题中,我们假设了一组线性关系,也就是如图所示
image

我们假定结果房价和这些参数之间有线性关系,即:
image

然后我们假定这个函数的损失函数为均方差,即:
image

那么就是说,我们现在是已知y和x,来求使得这个损失函数Loss最小化的一个w和b的组合

读取数据

点击查看代码
def load_data():
    # 从文件导入数据
    datafile = './work/housing.data'
    data = np.fromfile(datafile, sep=' ')

    # 每条数据包括14项,其中前面13项是影响因素,第14项是相应的房屋价格中位数
    feature_names = [ 'CRIM', 'ZN', 'INDUS', 'CHAS', 'NOX', 'RM', 'AGE', \
                      'DIS', 'RAD', 'TAX', 'PTRATIO', 'B', 'LSTAT', 'MEDV' ]
    feature_num = len(feature_names)

    # 将原始数据进行Reshape,变成[N, 14]这样的形状
    data = data.reshape([data.shape[0] // feature_num, feature_num])

    # 将原数据集拆分成训练集和测试集
    # 这里使用80%的数据做训练,20%的数据做测试
    # 测试集和训练集必须是没有交集的
    ratio = 0.8
    offset = int(data.shape[0] * ratio)
    training_data = data[:offset]

    # 计算训练集的最大值,最小值
    maximums, minimums = training_data.max(axis=0), training_data.min(axis=0)

    # 对数据进行归一化处理
    for i in range(feature_num):
        data[:, i] = (data[:, i] - minimums[i]) / (maximums[i] - minimums[i])

    # 训练集和测试集的划分比例
    training_data = data[:offset]
    test_data = data[offset:]
    return training_data, test_data

读取数据这里主要需要注意两点,就是对数据进行归一化。所谓归一化就是将指定变量按照其在整个数据集中的上下限,投影出一个0-1之间的值,并以此来作为计算的标准。这样做的好处是方便计算,而且后面能更好地评估值的影响。

还有一件事就是划分测试集和训练集,这里划分的比例是0.8,不过反正没用这个。

前向计算

什么叫前向计算呢?其实就是一个算出预测值的过程。我们由总的公式
image

我们取一个数据切片xj向量,然后随便取w和b,然后获得的结果就是预测值z,即:

点击查看代码
class Network(object):
    def __init__(self, num_of_weights):
        # 随机产生w的初始值
        # 为了保持程序每次运行结果的一致性,
        # 此处设置固定的随机数种子
        np.random.seed(0)
        self.w = np.random.randn(num_of_weights, 1)
        self.b = 0.
        
    def forward(self, x):
        z = np.dot(x, self.w) + self.b
        return z
net = Network(13)
x1 = x[0]
y1 = y[0]
z = net.forward(x1)
print(z)

损失函数

为什么要计算这个预测值呢?因为我们需要前向计算获得这个预测值,然后再进行损失函数的计算,用以评估这个预测值的结果好坏,即:

image
当然了,这里只是算了一组的预测值,我们当然是希望所有数据得到的损失函数的平均数,如图:

image

点击查看代码
class Network(object):
    def __init__(self, num_of_weights):
        # 随机产生w的初始值
        # 为了保持程序每次运行结果的一致性,此处设置固定的随机数种子
        np.random.seed(0)
        self.w = np.random.randn(num_of_weights, 1)
        self.b = 0.
        
    def forward(self, x):
        z = np.dot(x, self.w) + self.b
        return z
    
    def loss(self, z, y):
        error = z - y
        cost = error * error
        cost = np.mean(cost)
        return cost
		
net = Network(13)
# 此处可以一次性计算多个样本的预测值和损失函数
x1 = x[0:3]
y1 = y[0:3]
z = net.forward(x1)
print('predict: ', z)
loss = net.loss(z, y1)
print('loss:', loss)

计算梯度、梯度下降

什么是梯度?其实也很好理解,某个变量的梯度就是 : 预测值在这个变量上的偏导。之所以这么做是为了能通过梯度下降法寻找到极小值。

一般的做法是每次取w都令w = w -

  • step

画一个简单的图示:

image

在x'处的偏导大于0时,x-deta * y'会偏向极小值

同理

image

在x'处的偏导小于0时,x-deta * y'会偏向极小值

image

假设有一个极小值,那么这个梯度下降法就会让我们的这个x值逐渐移动到极小值附近停下。

这里需要注意一个问题,就是当我们的步长取得太小的话,那么迭代的次数就会过多,但是如果步长取得太大,就会导致迭代震荡,我们的x越来越往极小值外部偏,当然了会在某一个范围内稳定下来。

我们在计算梯度的时候,也就是在计算偏导,对于此题我们可以尝试计算一下:

image

image
image
image
image

综上我们这里就计算出来L关于b和L关于w 的偏导数,即

image
image

那么我们就可以在运算的过程中逐渐逐行的获得每一个wj组合的偏导,并让wj按照梯度下降的方式朝着L越来越小的方向发展

对此我们对类Network做出修改,如下:

点击查看代码
class Network(object):
    def __init__(self, num_of_weights):
        # 随机产生w的初始值
        # 为了保持程序每次运行结果的一致性,此处设置固定的随机数种子
        np.random.seed(0)
        self.w = np.random.randn(num_of_weights, 1)
        self.b = 0.
    #此处进行对于x值前向计算得到的预测值
    def forward(self, x):
        z = np.dot(x, self.w) + self.b
        return z
    #此处进行损失损失函数的计算
    def loss(self, z, y):
        error = z - y
        num_samples = error.shape[0]
        cost = error * error
        cost = np.sum(cost) / num_samples
        return cost
    #此处进行对于当前给定的w,x,y处的梯度计算
    def gradient(self, x, y):
        z = self.forward(x)
        gradient_w = (z-y)*x
        gradient_w = np.mean(gradient_w, axis=0)
        gradient_w = gradient_w[:, np.newaxis]
        gradient_b = (z - y)
        gradient_b = np.mean(gradient_b)        
        return gradient_w, gradient_b
    #此处用于进行梯度下降偏移
    def update(self, gradient_w, gradient_b, eta = 0.01):
        self.w = self.w - eta * gradient_w
        self.b = self.b - eta * gradient_b
    #此处进行实际训练,就是让所有数据按照梯度下降方向偏移直到结果 
    def train(self, x, y, iterations=100, eta=0.01):
        losses = []
        for i in range(iterations):
            z = self.forward(x)
            L = self.loss(z, y)
            gradient_w, gradient_b = self.gradient(x, y)
            self.update(gradient_w, gradient_b, eta)
            losses.append(L)
            if (i+1) % 10 == 0:
                print('iter {}, loss {}'.format(i, L))
        return losses

随机梯度下降

在搞懂了什么时梯度下降了之后,我们为了提升效率,就需要寻找一些优化方式,比较简单的方式就是随机梯度下降。

所谓的随机梯度下降也很好理解,就是对于波士顿房价预测这种线性关系比较明显的模型而言,我们往往不需要data那么大的数据量,而是可能几十行就可以解出一个效果比较好的解了,一个合理的解决方案是每次从总的数据集中随机抽取出小部分数据来代表整体,基于这部分数据计算梯度和损失来更新参数,这种方法被称作随机梯度下降法(Stochastic Gradient Descent,SGD)

最终整理以下代码,就是这样:

点击查看代码
import numpy as np

class Network(object):
    def __init__(self, num_of_weights):
        # 随机产生w的初始值
        # 为了保持程序每次运行结果的一致性,此处设置固定的随机数种子
        #np.random.seed(0)
        self.w = np.random.randn(num_of_weights, 1)
        self.b = 0.
        
    def forward(self, x):
        z = np.dot(x, self.w) + self.b
        return z
    
    def loss(self, z, y):
        error = z - y
        num_samples = error.shape[0]
        cost = error * error
        cost = np.sum(cost) / num_samples
        return cost
    
    def gradient(self, x, y):
        z = self.forward(x)
        N = x.shape[0]
        gradient_w = 1. / N * np.sum((z-y) * x, axis=0)
        gradient_w = gradient_w[:, np.newaxis]
        gradient_b = 1. / N * np.sum(z-y)
        return gradient_w, gradient_b
    
    def update(self, gradient_w, gradient_b, eta = 0.01):
        self.w = self.w - eta * gradient_w
        self.b = self.b - eta * gradient_b
            
                
    def train(self, training_data, num_epochs, batch_size=10, eta=0.01):
        n = len(training_data)
        losses = []
        for epoch_id in range(num_epochs):
            # 在每轮迭代开始之前,将训练数据的顺序随机打乱
            # 然后再按每次取batch_size条数据的方式取出
            np.random.shuffle(training_data)
            # 将训练数据进行拆分,每个mini_batch包含batch_size条的数据
            mini_batches = [training_data[k:k+batch_size] for k in range(0, n, batch_size)]
            for iter_id, mini_batch in enumerate(mini_batches):
                #print(self.w.shape)
                #print(self.b)
                x = mini_batch[:, :-1]
                y = mini_batch[:, -1:]
                a = self.forward(x)
                loss = self.loss(a, y)
                gradient_w, gradient_b = self.gradient(x, y)
                self.update(gradient_w, gradient_b, eta)
                losses.append(loss)
                print('Epoch {:3d} / iter {:3d}, loss = {:.4f}'.
                                 format(epoch_id, iter_id, loss))
        
        return losses

# 获取数据
train_data, test_data = load_data()

# 创建网络
net = Network(13)
# 启动训练
losses = net.train(train_data, num_epochs=50, batch_size=100, eta=0.1)

# 画出损失函数的变化趋势
plot_x = np.arange(len(losses))
plot_y = np.array(losses)
plt.plot(plot_x, plot_y)
plt.show()

小结:

我们在波士顿房价预测方案中,实际上做了三件事:

  1. 构建网络,初始化参数w和b,定义预测和损失函数的计算方法。
  2. 随机选择初始点,建立梯度的计算方法和参数更新方式。
  3. 从总的数据集中抽取部分数据作为一个mini_batch,计算梯度并更新参数,不断迭代直到损失函数几乎不再下降。

问题一:样本归一化:预测时的样本数据同样也需要归一化,但使用训练样本的均值和极值计算,这是为什么?

可以从三个角度理解:众所周知,我们的数据集分为训练集和测试集,对于测试集的均值方差归一化,不能用测试集的均值和方差,而要用训练集的均值和方差,因为真实数据中很难得到其均值和方差。另外,网络参数是从训练集学习到的,也就是说,网络的参数尺度是与训练集的特征尺度一致性相关的,所以应该认为测试数据和训练数据的特征分布一致。最后,训练集数据相比测试集数据更多,用于近似表征全体数据的分布情况。

总结就是认为测试数据的分布应该与训练数据的分布一致。

例如样本A、样本B作为一批样本计算均值和方差,与样本A、样本C和样本D作为一批样本计算均值和方差,得到的结果一般来说是不同的。那么样本A的预测结果就会变得不确定,这对预测过程来说是不合理的。解决方法是在训练过程中将大量样本的均值和方差保存下来,预测时直接使用保存好的值而不再重新计算。

标签:gradient,梯度,self,笔记,num,np,波士顿,data,def
From: https://www.cnblogs.com/Leventure/p/17921333.html

相关文章

  • Swift 笔记-1 基本类型,集合类型,控制流与基本函数
    目录基本类型变量与常量字符串单行多行整型浮点布尔值集合类型数组字典Dictionaries集合Sets枚举Enums控制流条件判断循环代码块抽象结构函数声明函数返回类型声明返回多个值自定义参数标签函数参数默认值函数与错误最近对iOS开发有兴趣,学习SwiftUI,主要跟的是hackingwiths......
  • 《软件需求十步走》阅读笔记四
    读到第四篇,就是需求工程的规划篇。   需求规划工作是面向“全业务、全信息、全系统”,业务是事项,采用分析综合、归纳演绎的逻辑方法整理出组织与对象的业务逻辑模型,在此业务的逻辑模型基础上进行系统的规划。也是事项的实作行为,也是对所做事项的总称。 业务研究就是借鉴科......
  • 【GUI软件】小红书详情数据批量采集,含笔记内容、转评赞藏等,支持多笔记同时采集!
    一、背景介绍1.1爬取目标您好!我是@马哥python说,一名10年程序猿。我用python开发了一个爬虫采集软件,可自动按笔记链接抓取笔记的详情数据。为什么有了源码还开发界面软件呢?方便不懂编程代码的小白用户使用,无需安装python,无需改代码,双击打开即用!软件界面截图:爬取结果截图:结......
  • 【GUI软件】小红书详情数据批量采集,含笔记内容、转评赞藏等,支持多笔记同时采集!
    目录一、背景介绍1.1爬取目标1.2演示视频1.3软件说明二、代码讲解2.1爬虫采集模块2.2软件界面模块2.3日志模块三、获取源码及软件一、背景介绍1.1爬取目标您好!我是@马哥python说,一名10年程序猿。我用python开发了一个爬虫采集软件,可自动按笔记链接抓取笔记的详情数据。......
  • 【学习笔记】使用科学和魔法。
    一直没有太理解我们是怎么上网的,今天逼着自己问了问GPT,这是他的回答。因为众所周知的原因,下文中“虚拟virtual私人private网络network”均用【数据删除】代替。连接WiFi:当用户在设备上连接WiFi时,他们实际上是连接到一个本地网络,这个网络由无线路由器提供。这个路由器......
  • ml.net例子笔记6-ml.net v2之AutoML
    AutoML1概念自动化机器学习也称为自动化ML或AutoML,是将机器学习模型开发过程中耗时的反复性任务自动化的过程。数据科学家、分析师和开发人员可以使用它来生成高度可缩放、高效且高产能的ML模型,同时保证模型的质量。https://learn.microsoft.com/zh-cn/dotnet/machine-l......
  • ml.net例子笔记7-ml.net与OMNX
    在整个模型生成过程中,模型位于内存中,并且可以在整个应用程序生命周期中访问。但是,一旦应用程序停止运行,而模型未在本地或远程的某个位置保存,则无法再访问该模型。通常情况下,在其他应用程序中训练模型之后,某些时候会使用模型进行推理或重新训练。因此,存储模型很重要。详细信息......
  • gnuradio笔记[1]-内嵌python代码块
    摘要在GNURadio中简单使用内嵌python代码块实现输出内容到文件.超链接解决无法编辑代码块内代码原理简介GNURadio简介[https://wiki.gnuradio.org/index.php?title=What_Is_GNU_Radio]GNURadioisafree&open-sourcesoftwaredevelopmenttoolkitthatprovidessig......
  • openGauss学习笔记-168 openGauss 数据库运维-备份与恢复-导入数据-使用gs_restore命
    openGauss学习笔记-168openGauss数据库运维-备份与恢复-导入数据-使用gs_restore命令导入数据168.1操作场景gs_restore是openGauss数据库提供的与gs_dump配套的导入工具。通过该工具,可将gs_dump导出的文件导入至数据库。gs_restore支持导入的文件格式包含自定义归档格式、目录......
  • 读程序员的README笔记17_构建可演进的架构(下)
    1. 可演进的API1.1. 随着需求的变化,你需要改变你的API,即代码之间的共享接口1.2. 改变API很容易,但很难做到正确1.3. 保持API小巧1.3.1. 小巧的API更易于理解和演进1.3.2. 只添加即刻需要的API方法或字段1.3.3. 带有许多字段的API方法应该有合理的默认值1.3.3.1. 开......