百度架构师深度学习课程 01 波士顿房价预测(含代码详解)

一、人工智能、机器学习、深度学习的关系

1、关系
概括来说,人工智能、机器学习和深度学习覆盖的技术范畴是逐层递减的。人工智能是最宽泛的概念。机器学习是当前比较有效的一种实现人工智能的方式。深度学习是机器学习算法中最热门的一个分支,近些年取得了显著的进展,并替代了大多数传统机器学习算法。
三者的关系如图所示,即:人工智能 > 机器学习 > 深度学习。

在这里插入图片描述

二、机器学习的实现

机器学习的实现可以分成两步:训练和预测,类似于我们熟悉的归纳和演绎:

  • 归纳:
    从具体案例中抽象一般规律,机器学习中的“训练”亦是如此。从一定数量的样本(已知模型输入X和模型输出Y)中,学习输出Y与输入X的关系(可以想象成是某种表达式)。
  • 演绎:
    从一般规律推导出具体案例的结果,机器学习中的“预测”亦是如此。基于训练得到的Y与X之间的关系,如出现新的输入X,计算出输出Y。通常情况下,如果通过模型计算的输出和真实场景的输出一致,则说明模型是有效的。

三、损失函数

  • 衡量模型预测值和真实值差距的评价函数也被称为损失函数(损失Loss)。
  • 优化算法:最小化损失是模型的优化目标,实现损失最小化的方法称为优化算法,也称为寻解算法(找到使得损失函数最小的参数解)
  • 模型假设、评价函数(损失/优化目标)和优化算法是构成模型的三个部分。

四、神经网络

人工神经网络包括多个神经网络层,如卷积层、全连接层、LSTM等,每一层又包括很多神经元,超过三层的非线性神经网络都可以被称为深度神经网络。

  • 神经元: 神经网络中每个节点称为神经元,由两部分组成:

     加权和:将所有输入加权求和。
     非线性变换(激活函数):加权和的结果经过一个非线性函数变换,让神经元计算具备非线性的能力。
    
  • 多层连接: 大量这样的节点按照不同的层次排布,形成多层的结构连接起来,即称为神经网络。 前向计算:
    从输入计算输出的过程,顺序从网络前至后。

  • 计算图: 以图形化的方式展现神经网络的计算逻辑又称为计算图。我们也可以将神经网络的计算图以公式的方式表达如下:
    Y=f3​(f2​(f1​(w1​⋅x1​+w2​⋅x2​+w3​⋅x3​+b)+…)…)…)

五、波士顿房价预测 (代码详解)

1.项目介绍

  • 波士顿房价预测是一个经典的机器学习任务,类似于程序员世界的“Hello World”
  • 波士顿地区的房价是由诸多因素影响的。该数据集统计了13种可能影响房价的因素和该类型房屋的均价,期望构建一个基于13个因素进行房价预测的模型,如 图所示。

图片来源百度飞桨

  • housing.data数据集

这是部分数据集,可在网上下载,用word打开,一共有506行,14列数据。

在这里插入图片描述

  • 房价是一个连续值,所以房价预测显然是一个回归任务。下面我们尝试用最简单的线性回归模型解决这个问题,并用神经网络来实现这个模型。
    在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

2.思路分析

来源:飞桨

3.代码实现

import numpy as np
import matplotlib.pyplot as plt

def load_data():
    # 从文件导入数据
    datafile = './work/housing.data'
    # fromfile方法读取文件,需要读取时知道存入文件时数组的维度和元素类型
    # a.tofile()和np.fromfile()需要配合使用可以通过元数据文件来存储额外信息
    #如果指定了sep参数,则fromfile()和tofile()将以文本格式对数组进行输入输出。
    #sep参数指定的是文本数据中数值的分隔符。
    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]这样的形状
    #shape[0]:表示矩阵的第二维长度,即行的长度,这里共506行
    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, avgs = training_data.max(axis=0), training_data.min(axis=0), \
                                 training_data.sum(axis=0) / training_data.shape[0]

    # 对数据进行归一化处理
    """
    运用平均数,将每个特征的取值缩放到-1~1之间。
    如果当前值为最大值,则处理后也为最大值,但不一定是1,
    如果当前为最小值,则处理后也为最小值,但不一定为-1,
    但是当前值为平均值时,处理后为0。
    """
    for i in range(feature_num):
        #print(maximums[i], minimums[i], avgs[i])
        #逗号“,”分隔各个维度,“:”表示各个维度内的切片,只有:表示取这个维度的全部值
        #data[:, i]遍历所有行的数据
        data[:, i] = (data[:, i] - avgs[i]) / (maximums[i] - minimums[i])

    # 训练集和测试集的划分比例
    training_data = data[:offset]
    test_data = data[offset:]
    return training_data, test_data
    
class Network(object):
    def __init__(self, num_of_weights):
        # 随机产生w的初始值
        #seed( ) 用于指定随机数生成时所用算法开始的整数值
        #如果使用相同的seed( )值,则每次生成的随即数都相同
        # 为了保持程序每次运行结果的一致性,
        # 此处设置固定的随机数种子
        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
            
    #  num_epoches:训练次数       batch_size:一次训练所选取的样本数     eta:学习率
    def train(self, training_data, num_epoches, batch_size=10, eta=0.01):
        n = len(training_data)
        losses = []
        for epoch_id in range(num_epoches):
            # 在每轮迭代开始之前,将训练数据的顺序随机打乱
            # 然后再按每次取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)]
            # enumerate() 函数用于将一个可遍历的数据对象(如列表、元组或字符串)组合为一个索引序列,
            # 同时列出数据和数据下标,一般用在 for 循环当中。
            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_epoches=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()

4.运行结果
在这里插入图片描述
在这里插入图片描述

5.代码解读
1.数据处理
数据处理包含五个部分:数据导入、数据形状变换、数据集划分、数据归一化处理和封装load data函数。数据预处理后,才能被模型调用。

(1). 数据导入

# 导入需要用到的package
import numpy as np
"""
json是java script object notation的缩写,用来存储和交换文本信息
使用时需要import json导入
json.dumps():将python对象编码为json的字符串
json.loads():将字符串编码为一个python对象
json.dump():将python对象序列化到一个文件,是文本文件,相当于将序列化后的json字符写到一个文件
json.load():从文件反序列表出python对象
总结:不带s的是序列化到文件或者从文件反序列化,带s的都是内存操作不涉及持久化
"""
import json
# 读入训练数据
datafile = './work/housing.data'
# fromfile方法读取文件,需要读取时知道存入文件时数组的维度和元素类型
# a.tofile()和np.fromfile()需要配合使用可以通过元数据文件来存储额外信息
# 如果指定了sep参数,则fromfile()和tofile()将以文本格式对数组进行输入输出。
# sep参数指定的是文本数据中数值的分隔符。
data = np.fromfile(datafile, sep=' ') 
data

(2). 数据形状变换
由于读入的原始数据是1维的,所有数据都连在一起。因此需要我们将数据的形状进行变换,形成一个2维的矩阵,每行为一个数据样本(14个值),每个数据样本包含13个X(影响房价的特征)和一个Y(该类型房屋的均价)。

# 读入之后的数据被转化成1维array,其中array的第0-13项是第一条数据,第14-27项是第二条数据,以此类推.... 
# 这里对原始数据做reshape,变成N x 14的形式
feature_names = [ 'CRIM', 'ZN', 'INDUS', 'CHAS', 'NOX', 'RM', 'AGE','DIS', 
                 'RAD', 'TAX', 'PTRATIO', 'B', 'LSTAT', 'MEDV' ]
feature_num = len(feature_names)
"""
shape函数可以了解数组的结构;reshape()函数改变数组的结构。
  w.shape[0]返回的是w的行数
  w.shape[1]返回的是w的列数
  df.shape():查看行数和列数
数组(矩阵)只有一个维度时,shape只有shape[0],返回的是该一维数组(矩阵)中元素的个数

data.shape[0] // feature_num:新矩阵的行数 506行
feature_num:新矩阵的列数
"""
data = data.reshape([data.shape[0] // feature_num, feature_num])

到这里我们可以查看一下数据输出

# 查看数据
# 查看矩阵第一行的数据
x = data[0]
print(x.shape)
print(x)

输出如下

(14,)

[6.320e-03 1.800e+01 2.310e+00 0.000e+00 5.380e-01 6.575e+00 6.520e+01
 4.090e+00 1.000e+00 2.960e+02 1.530e+01 3.969e+02 4.980e+00 2.400e+01]

(3). 数据集划分
将数据集划分成训练集和测试集,其中训练集用于确定模型的参数,测试集用于评判模型的效果

# 将80%的数据用作训练集,20%用作测试集
ratio = 0.8
offset = int(data.shape[0] * ratio) #shape[0]:表示矩阵的第二维长度
training_data = data[:offset]
training_data.shape

(4). 数据归一化处理
对每个特征进行归一化处理,使得每个特征的取值缩放到0~1之间。这样做有两个好处:一是模型训练更高效;二是特征前的权重大小可以代表该变量对预测结果的贡献度(因为每个特征值本身的范围相同)。

# 计算train数据集的最大值,最小值,平均值
# axis=0代表往跨行(down),而axis=1代表跨列(across)
maximums, minimums, avgs = \
                     training_data.max(axis=0), \
                     training_data.min(axis=0), \
     training_data.sum(axis=0) / training_data.shape[0]
# 对数据进行归一化处理
for i in range(feature_num):
    #print(maximums[i], minimums[i], avgs[i])
    """
    运用平均数,将每个特征的取值缩放到-1~1之间。
    如果当前值为最大值,则处理后也为最大值,但不一定是1,
    如果当前为最小值,则处理后也为最小值,但不一定为-1,
    但是当前值为平均值时,处理后为0。
    """
    # 遍历第i列所有元素
    data[:, i] = (data[:, i] - avgs[i]) / (maximums[i] - minimums[i])

在此,我们可以将上述几个数据处理操作封装成load data函数,以便下一步模型的调用

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, avgs = training_data.max(axis=0), training_data.min(axis=0), \
                                 training_data.sum(axis=0) / training_data.shape[0]

    # 对数据进行归一化处理
    for i in range(feature_num):
        #print(maximums[i], minimums[i], avgs[i])
        data[:, i] = (data[:, i] - avgs[i]) / (maximums[i] - minimums[i])
    #逗号“,”分隔各个维度,“:”表示各个维度内的切片,只有:表示取这个维度的全部值
    # 训练集和测试集的划分比例
    training_data = data[:offset]
    test_data = data[offset:]
    return training_data, test_data

# 获取数据
training_data, test_data = load_data()
# 获取除最后一列以外的所有行和列
x = training_data[:, :-1]
# 获取最后一列的所有元素
y = training_data[:, -1:]

# 查看数据
print(x[0])
print(y[0])

2.模型设计
模型设计是深度学习模型关键要素之一,也称为网络结构设计,相当于模型的假设空间,即实现模型“前向计算”(从输入到输出)的过程。
首先,假设我们以如下任意数字赋值参数做初始化:

w=[0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,−0.1,−0.2,−0.3,−0.4,0.0]
  • 5
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值