深度学习(一)

  • 人工智能AI、机器学习ML、深度学习DL的关系

人工智能(Artificial Intelligence, AI)是机器,特别是计算机系统对人类智能过程的模拟。人工智能是一个愿景,目标就是让机器像我们人类一样思考与行动,能够代替我们人类去做各种各样的工作。

实现人工智能的方法称为机器学习,简单来说就是让机器从历史数据中学习规律,然后训练出模型,使用模型预测未来的一种方法。

感觉机器学习的存在已经很合理了,为什么会有深度学习呢?

举个例子:一张猫的照片,我给机器,让它帮我识别这是猫还是狗?机器当然不能和我们人一样一眼就能看出来,我们需要提供一堆数据(也就是数据集)给机器,猫和狗在胡须、眼睛、鼻子等各方面的特征都不一样,所以机器就是通过这个特征区分出猫狗。那机器学习复杂的地方就在这,我们人提供给机器的数据,是事先对数据进行特征提取了的,也就是我们帮助机器把数据中的重点也指出来了。所以,人们需要事先确定哪些特征对于区分不同类别是重要的,并将这些特征从原始数据中提取出来。

当然,聪明的人类就想到能不能省点力,我直接把数据甩到机器脸上,让它自己提取特征,中间过程就是一个黑盒,我们什么都不用干,就把数据给机器就行了,等着它把分析的结果输出出来。所以,深度学习的引入改变了这种情况。深度学习使用神经网络模型,通过层层堆叠的方式自动学习数据中的特征表示。与传统机器学习中需要手动提取特征,深度学习可以从原始数据中端到端地学习特征和模式。对于图像识别任务,深度学习模型可以自动学习到眼睛、鼻子、胡须等特征,并从中区分猫和狗,无需人为定义这些特征。但是深度学习相对于机器学习,也有它的缺点,我想让预测结果更加准确,我需要喂给机器大量的数据,谁让我们偷懒不帮机器提取特征呢。并且训练也会比机器学习花的时间多,因为一张猫的图片中只有部分特征是有用的,就比方说眼睛、鼻子、胡须,其他都是无用的数据,机器学习拿到的数据已经是重点提示了这些特征是有用特征,所以训练花的时间就会短一些,而深度学习模型需要自己提取,其中有大量无用的数据,这样的工作量就比较大。深度学习模型通常由多个层级组成,每一层都可以学习不同抽象级别的特征。通过逐层的特征提取和组合,模型能够自动发现更有信息量的特征。低层的特征可能对高层次任务并不直接有用,模型可以通过反向传播算法,在训练过程中调整权重以减少对无用特征的依赖。

  • 什么是神经网络?

可以查看这个视频,简单快速地对神经网络有一个清晰的认知。

图解,卷积神经网络(CNN可视化)_哔哩哔哩_bilibiliicon-default.png?t=N7T8https://www.bilibili.com/video/BV1x44y1P7s2/?spm_id_from=pageDriver&vd_source=d49e1e9eeb0873d718278b65fb99f937我先举一个最简单的例子,房价预测吧。通过面积、地理位置、厕所的数量去估计某个房子的价格,这三个特征具有不同的权重,通过相乘再相加,最终可以得到房价,但是这个房价可能不准确,所以需要大量得数据去一直拟合,最后将w1、w2、w3调整到一个很合适的权重,所以整个训练过程就是前向传播,得到结果,发现结果不精准,然后再反向传播,一直调整参数,最后训练出一个精度比较高的模型,然后我们就可以拿着这个模型来预测房价啦!(这是最简单神经网络,主要起到一个理解的作用,反正我自己就是这样理解的。)

进行一个详细一点的分析吧,下图是一个28×28圆形的图片,28×28一共是784个像素块,圆形区域,我们可以用1表示,背景不重要,我们可以用0表示,然后将28×28拉成一个784的一维向量,输入到神经网络中,每个像素点都有不同的权重,我们进行相乘再相加,将结果送入下一层神经元中,进行激活,好的,什么是激活???字面意思,就是通过计算,上一层的结果如果满足某种条件,说明这种特征是有用的,那我就激活,但是有些体征提取出来一点用都没用,那就不需要传到下一层了,最后是根据输出层的结果来进行分类,结果当然是一个概率啊,可以看到最后方形的概率是最大的0.5???明明是个圆形,但是这个模型给我预测错了。这个时候就需要进行改错了,又通过最后一层反向传播回去,一层一层传回去,进行一个权重的更新,多轮调整以后,这个模型的精度就上去了。

所以说深度学习整个模型就是一个黑盒,一旦设置好模型,给一定的数据集,进行训练,它自己就开始调整参数,最后达到一个比较理想的结果,然后我们人再拿着这个模型去给输入,它就给我们一个输出。

  • 机器学习中的两大问题(分类与回归)

回归和分类问题是机器学习中两个基本的问题类型,其主要区别在于输出变量的类型。

回归问题是指预测一个连续的数值输出。例如,根据一些特征(如房屋面积、位置等),预测该房屋的价格。在回归问题中,模型需要学习输入变量与输出变量之间的非线性或线性关系,目标是预测一个连续的输出。

分类问题是指预测一个离散的类别输出。例如,根据一些特征(如图像像素、声音频率等)将图像或声音分为不同的类别(如人脸识别、语音识别等)。在分类问题中,模型需要学习输入变量与输出变量之间的非线性或线性关系,目标是预测数据所属的类别。

  • 线性回归

可以先看我的介绍,如果看不懂,可以再去看一下吴恩达的视频,讲的还是比较通透的。3.1 线性回归模型-part-1_哔哩哔哩_bilibiliicon-default.png?t=N7T8https://www.bilibili.com/video/BV1Pa411X76s?p=9&vd_source=d49e1e9eeb0873d718278b65fb99f937线性回归是回归问题中,最经典也是最简单的例子。

这次我们还是买房子,在买房子之前,我们想要提前预估一下房价对吧,那我们肯定需要拿到以前的数据做参考,如下图,一堆散乱的点,看着很乱,但是我们希望通过一个线性方程f(x)=wx+b去拟合出一条直线,然后通过给定一个输入量x,让其输出一个看着近似完美的输出值f(x),这样更方便我们买房子。

整个过程就是这样,先给训练集,也就是一些前人买房留下来的真实数据,然后我们构造出一个算法,让机器按照特定的算法和数据中的特征开始自己学习,最后还给我们一个模型,此时,我们只需要给输入值x,然后机器就会还给我们一个预测值f(x),那这个线性方程中有两个重要的参数w和b,这两参数可以决定这个线的位置,所以我们要找到一组比较完美的w和b,使得整个模型的精度看起来好。后面我们将说明如何找到一个比较好的w和b。

我们先从残差说起。残差说白了就是真实值和预测值间的差值(也可以理解为差距、距离),用公式表示是:

如果让直线更好的拟合,那这个残差e一定是越小越好,因为有多个点,有些点在直线的上面,那残差就是一个正数,有些点在直线的下面,那残差是一个负数,通过平方,我们可以将负号消除掉。对于我们数据中的每个点如此计算一遍,再将所有的 e^2相加,就能量化出拟合的直线和实际之间的误差。用公式表达如下,将每个点的残差算出来然后平方再相加,这个就是残差平法和,也就是最常用的损失函数损失函数是衡量回归模型误差的函数,也就是我们要的“直线”的评价标准。这个函数的值越小,说明直线越能拟合我们的数据。

下面是一个简单的拟合过程,附带损失函数(残差平方和只是其中用的比较多的一种损失函数)计算的过程,简单来说,就是损失函数计算出来的值越小越好,找到最小损失值就行。下图w=1的时候,损失值为0,达到最好拟合效果。

下面是随机生成了512个点,然后构造线性回归模型去拟合直线的代码,拟合的过程是一个动图,可以拿着作为小demo体验一下。

"""
线性回归模型   y=kx+b
"""
import torch
import torch.nn as nn
import matplotlib.pyplot as plt

# 准备数据集  在y=3x+10这个直线的附件随机生成300个点
x = torch.rand(512)
noise = 0.2*torch.randn(x.size())
k = 3
b = 10
y = k*x+b+noise

# 设计线性模型
class LinearModel(nn.Module):
    def __init__(self, in_fea, out_fea):
        super(LinearModel, self).__init__()
        self.out = nn.Linear(in_features=in_fea, out_features=out_fea)

    def forward(self, x):
        x = self.out(x)
        return x

input_x = torch.unsqueeze(x, dim=1)
input_y = torch.unsqueeze(y, dim=1)

model = LinearModel(1, 1)

# 构造损失函数和优化器的选择
loss_func = nn.MSELoss() # MSE损失函数就是残差平方和函数
optimizer = torch.optim.SGD(model.parameters(), lr=0.02) # 采用随机梯度下降

plt.ion()
for epoch in range(200):
    prediction = model(input_x)
    loss = loss_func(prediction, input_y)
    optimizer.zero_grad()
    loss.backward()
    optimizer.step()

    if epoch % 10 == 0:
        plt.cla()
        plt.scatter(input_x.data.numpy(), input_y.data.numpy())
        plt.plot(input_x.data.numpy(), prediction.data.numpy(), 'r-', lw=5)
        plt.xlim(0, 1.1)
        plt.ylim(0, 20)
        [w, b] = model.parameters()
        plt.text(0, 0.5, 'loss=%.4f, k=%.2f, b=%2f'%(loss.item(), w.item(), b.item() ),fontdict={'size': 20, 'color':  'red'})
        plt.pause(0.5)
plt.ioff()
plt.show()

运行结果如下图:

  • 逻辑回归

逻辑回归是一种分类算法,用于处理二分类问题。它通过拟合一个逻辑函数(如 sigmoid 函数),将输入特征映射到0和1之间的概率值。这个概率值可以被解释为预测样本属于某个类别的概率。通常将输出概率值大于或等于0.5的样本划分为正类,小于0.5的样本划分为负类。

其实可以很明显的看出,线性回归的输出是连续的。

 

逻辑回归可以看作是将线性组合的结果通过sigmoid函数进行非线性变换,从而得到输入特征映射到0和1之间的概率输出,进而达到分类的效果。下面进行了更详细的分析。

下面有一个二分类的代码,可以参考一下。

import torch
import torch.nn.modules
import torch.nn
import numpy as np
from torch.autograd import Variable  # torch的基本变量
import torch.nn.functional as F  # 里面有很多torch的函数
import matplotlib.pyplot as plt


# 定义自带forward propagation的神经网络。
class Net(torch.nn.Module):
    def __init__(self, n_features, n_hiddens, n_outputs):
        super(Net, self).__init__()
        self.hidden = torch.nn.Linear(n_features, n_hiddens)
        self.predict = torch.nn.Linear(n_hiddens, n_outputs)

    def forward(self, x):
        x = F.relu(self.hidden(x))
        predict = F.softmax(self.predict(x))
        return predict


class MyNet:
    def __init__(self, n_features, n_hiddens, n_outputs, times):
        self.NeuronalNet = Net(n_features, n_hiddens, n_outputs)
        self.realX = None
        self.realY = None
        self.opitimizer = None
        self.lossFunc = None
        self.times = times

    # 训练集
    def getData(self):
        temp = torch.ones(100, 2)

        B = torch.normal(2 * temp, 1)

        By = torch.ones(100)
        A = torch.normal(-2 * temp, 1)
        Ay = torch.zeros(100)

        self.realX = Variable(torch.cat([A, B], 0))
        self.realY = Variable(torch.cat([Ay, By]).type(torch.LongTensor))

        # plt.scatter(realX.data.numpy()[:,0],realX.data.numpy()[:,1],c=realY)
        # plt.show()

    def run(self):
        self.opitimizer = torch.optim.SGD(self.NeuronalNet.parameters(), lr=0.01)
        self.lossFunc = torch.nn.CrossEntropyLoss()

        for i in range(self.times):
            out = self.NeuronalNet(self.realX)

            loss = self.lossFunc(out, self.realY)

            self.opitimizer.zero_grad()

            loss.backward()

            self.opitimizer.step()

    # 可视化
    def showBoundary(self):
        x_min, x_max = self.realX[:, 0].min() - 0.1, self.realX[:, 0].max() + 0.1
        y_min, y_max = self.realX[:, 1].min() - 0.1, self.realX[:, 1].max() + 0.1
        xx, yy = np.meshgrid(np.linspace(x_min, x_max, 101), np.linspace(y_min, y_max, 101))
        cmap = plt.cm.Spectral

        X_test = torch.from_numpy(np.c_[xx.ravel(), yy.ravel()]).float()
        y_pred = self.NeuronalNet(X_test)
        _, y_pred = y_pred.max(dim=1)
        y_pred = y_pred.reshape(xx.shape)

        plt.contourf(xx, yy, y_pred, cmap=plt.cm.Spectral, alpha=0.8)
        plt.scatter(self.realX[:, 0], self.realX[:, 1], c=self.realY, s=40, cmap=plt.cm.RdYlBu)
        plt.xlim(xx.min(), xx.max())
        plt.ylim(yy.min(), yy.max())
        plt.title("binary classifier")
        plt.show()

    def predict(self, inputData):
        # inputData should be a 1x2 matrix
        data = torch.from_numpy(np.array(inputData)).int()
        return self.NeuronalNet(data.float())


if __name__ == "__main__":
    myNet = MyNet(2, 18, 2, 1000)
    myNet.getData()
    myNet.run()
    myNet.showBoundary()
    probabilitys = list(myNet.predict([3, 3]).data.numpy())
    print("是第{0}类".format(1 + probabilitys.index(max(probabilitys))))






运行结果如下图:

  • Softmax回归

这个视频讲解很便于理解,可以大概的了解一下softmax回归。

3.18 Softmax回归_哔哩哔哩_bilibiliicon-default.png?t=N7T8https://www.bilibili.com/video/BV1g14y1e7B2/?spm_id_from=333.788&vd_source=d49e1e9eeb0873d718278b65fb99f937

逻辑回归对应的是二分类问题,而softmax回归对应的是一个多分类问题。

就拿下图的例子来讲,一共有四种动物类型,现在给一张狗的图片作为输入,通过多层神经网络的特征提取以后,到达最后一层softmax层,这一层的作用是什么???是进行一个概率的评估,输出一个向量[0.75  0.1  0.1  0.05],也就是输出每一种类型的概率,0.75是最大概率,最后的预测结果就是对应的动物类型。

利用MNIST数据集,完成0-9的十分类数字识别,代码可以之间run。

import torch
from torch.utils.data import DataLoader
from torchvision import transforms
from torchvision.datasets import MNIST
import matplotlib.pyplot as plt


class Net(torch.nn.Module):

    def __init__(self):
        super().__init__()
        self.fc1 = torch.nn.Linear(28 * 28, 64)
        self.fc2 = torch.nn.Linear(64, 64)
        self.fc3 = torch.nn.Linear(64, 64)
        self.fc4 = torch.nn.Linear(64, 10)

    def forward(self, x):
        x = torch.nn.functional.relu(self.fc1(x))
        x = torch.nn.functional.relu(self.fc2(x))
        x = torch.nn.functional.relu(self.fc3(x))
        x = torch.nn.functional.log_softmax(self.fc4(x), dim=1)
        return x


def get_data_loader(is_train):
    to_tensor = transforms.Compose([transforms.ToTensor()])
    data_set = MNIST("", is_train, transform=to_tensor, download=True)
    return DataLoader(data_set, batch_size=15, shuffle=True)


def evaluate(test_data, net):
    n_correct = 0
    n_total = 0
    with torch.no_grad():
        for (x, y) in test_data:
            outputs = net.forward(x.view(-1, 28 * 28))
            for i, output in enumerate(outputs):
                if torch.argmax(output) == y[i]:
                    n_correct += 1
                n_total += 1
    return n_correct / n_total


def main():
    train_data = get_data_loader(is_train=True)
    test_data = get_data_loader(is_train=False)
    net = Net()

    print("initial accuracy:", evaluate(test_data, net))
    optimizer = torch.optim.Adam(net.parameters(), lr=0.001)
    for epoch in range(2):
        for (x, y) in train_data:
            net.zero_grad()
            output = net.forward(x.view(-1, 28 * 28))
            loss = torch.nn.functional.nll_loss(output, y)
            loss.backward()
            optimizer.step()
        print("epoch", epoch, "accuracy:", evaluate(test_data, net))

    for (n, (x, _)) in enumerate(test_data):
        if n > 3:
            break
        predict = torch.argmax(net.forward(x[0].view(-1, 28 * 28)))
        plt.figure(n)
        plt.imshow(x[0].view(28, 28))
        plt.title("prediction: " + str(int(predict)))
    plt.show()


if __name__ == "__main__":
    main()

运行结果如下:

可以看见两次预测的精度都还是蛮高的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值