19. 深度学习 - 用函数解决问题

news2024/12/26 10:54:47

文章目录


茶桁的AI秘籍 核心基础 19

Hi, 你好。我是茶桁。

上一节课,我们从一个波士顿房价的预测开始写代码,写到了KNN。

之前咱们机器学习课程中有讲到KNN这个算法,分析过其优点和缺点,说起来,KNN这种方法比较低效,在数据量比较大的时候就比较明显。

那本节课,我们就来看一下更加有效的学习方法是什么,A more Efficient Learning Way.

接着我们上节课的代码我们继续啊,有不太了解的先回到上节课里去看一下。

我们X_rmy如果能够找到这两者之间的函数关系,每次要计算的时候,输入给这个函数,就能直接获得预测值。

那这个函数关系怎么获得呢?我们需要先观察一下,这个时候就到了我们的拟合函数关系。

那既然要观察,当然最好就是将数据可视化之后进行观察:

import matplotlib.pyplot as plt
plt.scatter(X_rm, y)

Alt text

可以看到,它们之间的关系大体应该这样一种关系:

在这里插入图片描述

那这个样子的图我们熟悉不? 是不是在线性回归那一张里我们见过?也就是用一根直线去拟合了这些点的一个趋势。

我们把它写出来:

f ( x ) = k ⋅ r m + b f(x) = k \cdot rm + b f(x)=krm+b

那我们现在就会把这个问题变成,假设现在的函数k*rm+b, 那我们就需要找到一组k和b,然后让它的拟合效果最好。这个时候我们就会遇到一个问题,拟合效果怎样算是好?

比方说我们现在有一组数据,一组实际的值,还有一组预测值。

real_y = {3, 6, 7}
y_hats = {3, 4, 7}
y_hats2 = {3, 6, 6}

问哪个值更好。

我们会发现这两个预测都挺好的,那哪个更好?这个时候我们需要搬出我们的loss函数了。

loss函数就是在我们进行预测的时候,它的信息损失了多少,所以我们称其为损失函数,loss函数。
l o s s ( y , y ^ ) = 1 N ∑ i ∈ N ( y i − y i ^ ) 2 loss(y, \hat y) = \frac{1}{N}{\sum_{i \in N}}(y_i - \hat {y_i})^2 loss(y,y^)=N1iN(yiyi^)2
y_i - yhat_i这个值越接近于0。 等于0的意思就是每一个预测的y都和实际的y的值是一样的。那么如果这个值越大指的是预测的y和实际的y之间差的越大。

那我们在这个地方就可以定义一个函数:

def loss(y, yhat):
    return np.mean((np.array(y) - np.array(yhat))** 2)

然后我们直接将两组yhat和真实的real_y代入进去比对:

loss(real_y, y_hats)
loss(real_y, y_hats2)

---
1.3333333333333333
0.3333333333333333

所以它这个意思是说yhats2的效果更好一些。

那我们将上面这个loss函数就叫做Mean Squared Error,就是均方误差,也简称MSE。咱们现在有了loss,就有了是非判断的标准了,就可以找到最好的结果。

有了判断标准怎么样来获得最优的k和b呢?早些年的时候有这么几种方法,第一种是直接用微积分的方法做计算。
l o s s = 1 N ∑ i ∈ N ( y i − y ^ ) 2 = 1 N ∑ i ∈ N ( y i − ( k x i + b ) ) 2 \begin{align*} loss & = \frac{1}{N}\sum_{i\in N}(y_i - \hat y)^2 \\ & = \frac{1}{N}\sum_{i\in N}(y_i - (kx_i + b))^2 \\ \end{align*} loss=N1iN(yiy^)2=N1iN(yi(kxi+b))2
此时我们是知道x_i 和y_i的值,N也是常数。那么其实求偏导之后它就可以变化成下面这组式子:
A k 2 + B k + C A ′ b 2 + B ′ b + C ′ Ak^2 + Bk +C \\ A'b^2+B'b+C' Ak2+Bk+CAb2+Bb+C
A、B、C是根据我们所知道的x_i和y_i以及常数N来计算出来的数。这个时候loss要取极值的时候,我们令其为loss’, 那loss’就等于-A/2B,或者-A’/2B’。那么这种方法我们就称之为最小二乘法,它是为了最小化MSE,对MSE求偏导数并令其等于零,来找到使MSE最小的参数值。

但是为什么后来人们没有用微积方的方法直接做呢?是因为这个函数会变得很复杂,当函数变得极其复杂的时候,学过微积分的同学就应该知道,你是不能直接求出来他的导数的。也就是说当函数变得极其复杂的时候,直接用微积分是求不出来极致点的,所以这种方法后来就没用。

第二种方法,后来人们想了可以用随机模拟的方法来做。

我们首先来在-100到100之间随机两个值:k和b

VAR_MAX, VAR_MIN = 100, -100
k, b = random.randint(VAR_MIN, VAR_MAX), random.randint(VAR_MIN, VAR_MAX)

只拿到一组当然是无从比较的,所以我们决定拿个100组的随机值:

total_times = 100
for t in range(total_times):
    k, b = random.randint(VAR_MIN, VAR_MAX), random.randint(VAR_MIN, VAR_MAX)

然后定义一个值, 叫做最小的loss。这个最小的loss一开始取值为无穷大,并且再给两个值,最好的k和最好的b,先赋值为None

min_loss = float('inf')
best_k, best_b = None, None

之后我们要拿预测值来赋值给新的loss,我们来定义一个函数,它要做的事情很简单,就是返回k*x+b

def model(x, k, b):
    return k*x + b

loss_ = loss(y, model(X_rm, k, b))

接着我们就可以来进行对比了,就会找到那组最好的k和b:

if loss_ < min_loss:
    min_loss = loss_
    best_k, best_b = k, b

完整的代码如下, 当然我们是接着之前的代码写的,所以loss函数和y,还有X_rm都是在之前代码中有过定义的。

VAR_MAX, VAR_MIN = 100, -100
min_loss = float('inf')
best_k, best_b = None, None

def model(x, k, b):
    return x * k +b

total_times = 100

for t in range(total_times):
    k, b = random.randint(VAR_MIN, VAR_MAX), random.randint(VAR_MIN,VAR_MAX)

    loss_ = loss(y, model(X_rm, k, b))

    if loss_ < min_loss:
        min_loss = loss_
        best_k, best_b = k, b
        print("在{}时刻找到了更好的k: {}, b: {}, 这个loss是:{}".format(t, k, b, loss_))

---0时刻找到了更好的k: 12, b: 89, 这个loss是:20178.468824442698时刻找到了更好的k: 2, b: 2, 这个loss是:131.8700051146245221时刻找到了更好的k: 11, b: -48, 这个loss是:47.340357088932805

如果我们将寻找的次数放大,改为10**3, 那我们会发现,开始找的很快,但是后面寻找的会越来越慢。

就类似于你现在在一个公司,假设你从刚进去的时候,要达到职位很高,薪水很高。小职员你想一直升职,你可以随机的去做很多你喜欢做的事情,没有人指导你。一开始的时候,你会发觉自己的升职加薪似乎并没有那么困难,但是随着自己越往上,升职的速度就降下来了,因为上面职位并没有那么多了。这个时候你所需要尝试和努力就会越来越多。到后面你每尝试一步,你所需要的努力就会越来越多。

那么这个时候我们就要想,我们怎么样能够让更新频率更快呢?而不要像这样到后面基本上不更新了。

不知道我们是否还记得大学时候的数学知识,假设现在这个loss和k在一个二维平面上,我们对loss和k来求一个偏导:

∂ l o s s ∂ k \frac{\partial loss}{\partial k} kloss

这个导数的取值范围就会导致两种情况,当其大于0的时候,k越大,则loss也越大,当其小于0的时候,k越大,loss则越小。

那我们在这里就可以总结出一个规律:

p ′ = p + ( − 1 ) ∂ l o s s ∂ p ∗ α p' = p + (-1)\frac{\partial loss}{\partial p} * \alpha p=p+(1)plossα

α \alpha α就是一个很小的数,因为我们每次要只能移动很小的一点,不能减小很多。

那有了这个,我们就可以将我们的k和b应用上去,也就可以得到:

k ′ = k + ( − 1 ) ∂ l o s s ∂ k ⋅ α b ′ = b + ( − 1 ) ∂ l o s s ∂ b ⋅ α \begin{align*} k' = k + (-1)\frac{\partial loss}{\partial k} \cdot \alpha \\ b' = b + (-1)\frac{\partial loss}{\partial b} \cdot \alpha \\ \end{align*} k=k+(1)klossαb=b+(1)blossα

那我们如何使用计算机来实现刚刚讲的这些内容呢?我们先把上面的式子再做一下变化:

k n + 1 = k n + − 1 ⋅ ∂ l o s s ( k , b ) ∂ k n b n + 1 = b n + − 1 ⋅ ∂ l o s s ( b , b ) ∂ b n k_{n+1} = k_n + -1 \cdot \frac{\partial loss(k, b)}{\partial k_n} \\ b_{n+1} = b_n + -1 \cdot \frac{\partial loss(b, b)}{\partial b_n} kn+1=kn+1knloss(k,b)bn+1=bn+1bnloss(b,b)

这个就是所谓的梯度下降。

那现在的问题就变成,如何使用计算机来实现梯度下降。我们就来定义两个求导函数,并且将之前的代码拿过来做一些修改:

def loss(y, yhat):
    return np.mean((np.array(y) - np.array(yhat)) ** 2)

def partial_k(x, y, k_n, b_n):
    return 2 * np.mean((y - (k * x + b))*(-x))

def partial_b(x, y, k_n, b_n):
    return 2 * np.mean((y - (k * x + b))*(-1))

k,b = random.random(), random.random()

min_loss = float('inf')
best_k, best_b = None, None

total_times = 500
alpha = 1e-3

k_b_history = []

for t in range(total_times):
    k = k + (-1) * partial_k(X_rm, y, k, b) * alpha 
    b = b + (-1) * partial_b(X_rm, y, k, b) * alpha

    loss_ = loss(y, model(X_rm, k, b))

    if loss_ < min_loss:
        min_loss = loss_
        best_k, best_b = k, b
        k_b_history.append([best_k, best_b])
        print("在{}时刻找到了更好的k: {}, b: {}, 这个loss是:{}".format(t, k, b, loss_))

---0时刻找到了更好的k: 0.8391888851738278, b: 0.44333100376779605, 这个loss是:360.0001031761941时刻找到了更好的k: 1.0586893752129705, b: 0.474203003102507, 这个loss是:312.7942150454931
...498时刻找到了更好的k: 3.587603582169745, b: 0.40777844839877003, 这个loss是:58.761172062586965499时刻找到了更好的k: 3.587736446932306, b: 0.4069332804559017, 这个loss是:58.760441520932375

其实关于这个内容,我们在机器学习 - 线性回归那一章就介绍过。看不懂这一段的小伙伴可以回过头取好好看一下那一章。

那这样,我们可以发现,之前是间隔很多次才作一词更新,而现在是每一次都会进行更新,一直在减小。这个是因为我们实现了一个「监督」。

在这样的情况下结果就变得更好了,比如我们再将次数调高一点,在全部运行完之后,我们来画个图看看:

plt.scatter(X_rm, y)
plt.scatter(X_rm, best_k * X_rm + best_b, color='orange')
plt.plot(X_rm, best_k * X_rm + best_b, color='red')

Alt text

我们可以看到它拟合出来的点和连接成的直线,和我们上面手动去画的似乎还是有很大差别的。

在刚才的代码里我还做了一件事情,定义了一个k_b_history, 然后将所有的best_k和best_b都存储到了里面。然后我们随机取几个点,第一个取第10个测试点,第二个取第50次测试点,第三个我们取第5000次,第四个我们取最后一次:

test_0, test_1, test_2, test_3, test_4 = 0, 10, 50, 5000, -1

然后我们分别画一下这几个点的图:

plt.scatter(X_rm, y)
plt.scatter(X_rm, k_b_history[test_0][0] * X_rm + k_b_history[test_0][1])
plt.scatter(X_rm, k_b_history[test_1][0] * X_rm + k_b_history[test_1][1])
plt.scatter(X_rm, k_b_history[test_2][0] * X_rm + k_b_history[test_2][1])
plt.scatter(X_rm, k_b_history[test_3][0] * X_rm + k_b_history[test_3][1])
plt.scatter(X_rm, k_b_history[test_4][0] * X_rm + k_b_history[test_4][1])

Alt text

我们就可以看到,刚开始的时候和最后的一次拟合的线的结果,还有中间一步步的拟合的变化。这条线在往上面一步一步的走。这样我们相当于是透视了它整个获得最优的k和b的过程。

那这个时候我们来看一下,咱们怎么怎么预测呢?我们可以拿我们的best_kbest_b去输出最后的预测值了:

model(7, best_k, best_b)

---
28.718752244698216

预测出来是28.7万。那房间数目为7的时候,我们预测出这个价格是28.7万,还记得咱们上节课中用KNN预测出来的值么?

find_price_by_simila(rm_to_price, 7)

---
29.233333333333334

是29万对吧?现在我们就能看到了,这两种方式预测值基本很接近,都能预测。

那么我们使用函数来进行预测的原因还有一个,就是我们在使用函数在进行学习之后,然后拿模型去计算最后的值,这个计算过程速度会快很多。

好,咱们下节课将会学习怎样拟合更加复杂的函数,因为这个世界上的函数可不仅仅是最简单线性,还得拟合更加复杂的函数。

然后再后面的课程,我们会讲到激活函数,开始接触神经网络,什么是深度学习。

然后我们要来讲解一个很重要的概念,就是反向传播,会讲怎么样实现自动的反向传播。实现了自动的反向传播,我们会基于拓普排序的方法让计算机能够自动的计算它的梯度和偏导。

在讲完这些之后,基本上我们就有了构建一个深度学习神经网络框架的内容了。

好,希望小伙伴们在今天的课程中有所收获。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1194079.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【前端】TypeScript核心知识点讲解

1.TypeScript简介及入门案例 &#xff08;1&#xff09;什么是TypeScript&#xff1f; TypeScript 是 JavaScript 的一个超集&#xff0c;支持 ECMAScript 6 &#xff08;ES6&#xff09;标准。 TypeScript 由微软开发的自由和开源的编程语言。 TypeScript 设计目标是开发大…

ubuntu 16.04.5 安装 vivado 2019.1 完整编译AD9361的环境

一、前期安装 1、安装ncurses库&#xff08;已经包含了&#xff0c;其他的os需要安装&#xff09; sudo apt install libncurses5二、安装 sudo ./xsetup使用lic进行激活。 三、安装后 输入指令 sudo gedit ~/.bashrc 末尾添加 source /opt/Xilinx/Vivado/2019.1/setti…

串口通信(11)-CRC校验介绍算法

本文为博主 日月同辉&#xff0c;与我共生&#xff0c;csdn原创首发。希望看完后能对你有所帮助&#xff0c;不足之处请指正&#xff01;一起交流学习&#xff0c;共同进步&#xff01; > 发布人&#xff1a;日月同辉,与我共生_单片机-CSDN博客 > 欢迎你为独创博主日月同…

条件渲染-if/hidden

1.条件渲染的方法 1.1 wx&#xff1a;if方法&#xff08;少用&#xff09; block只是一个容器&#xff0c;容器内的都用于if判断 1.2 hidden方法 注&#xff1a;view要小写&#xff0c;否则不生效 1.3方法对比

【狂神说Java】Dubbo + Zookeeper

✅作者简介&#xff1a;CSDN内容合伙人、信息安全专业在校大学生&#x1f3c6; &#x1f525;系列专栏 &#xff1a;狂神说Java &#x1f4c3;新人博主 &#xff1a;欢迎点赞收藏关注&#xff0c;会回访&#xff01; &#x1f4ac;舞台再大&#xff0c;你不上台&#xff0c;永远…

【java:牛客每日三十题总结-4】

java:牛客每日三十题总结 总结如下 总结如下 集合相关知识点 元素是否排序和插入顺序无关&#xff0c;取决与集合实现是否考虑了传入对象的java.lang.Comparable接口抽象类和接口相关知识 只能说越来越抽象了 java线程通信的方式 在Java中&#xff0c;常用的线程通信方式有两…

轻松下载网页音频和视频

在网页上看到好看的视频或者听到的音乐想保存&#xff0c;让我来教你&#xff08;仅供学习&#xff09; 注意&#xff1a;有极少部分的网站视频经过加密&#xff0c;无法下载 一、视频下载 1.打开视频网页 2.右键“检查” 3.刷新网页 4.按照下图中步骤操作 5.把复制的链接放…

【漏洞复现】BYTEVALUE智能流控路由器存在命令执行

【漏洞介绍】 百为智能流控路由器 /goform/webRead/open 路由的 ?path 参数存在有回显的命令注入漏洞。攻击者可通过该漏洞在服务器端执行命令&#xff0c;写入后门&#xff0c;获取服务器权限&#xff0c;从而获取路由器权限。 【指纹】 title”BYTEVALUE 智能流控路由器”…

C语言C位出道心法(五):内存管理

C语言C位出道心法(一):基础语法 C语言C位出道心法(二):结构体|结构体指针|链表 C语言C位出道心法(三):共用体|枚举 C语言C位出道心法(四):文件操作 C语言C位出道心法(五):内存管理 一:C语言内存管理认知 二:C语言中内存堆|栈认知 三:C语言中引用内存丢失认知

数字档案馆实施方案需要考虑哪些因素

数字档案馆实施方案通常需要考虑以下几个方面&#xff1a; 1. 硬件和软件设备的选择&#xff1a;需要根据数字档案馆的规模和需求选购适当的硬件和软件设备&#xff0c;包括服务器、存储设备、数据库、操作系统、专久智能档案管理系统等。 2. 数据的采集和处理&#xff1a;确定…

idea中搭建Spring boot项目(借助Spring Initializer)

创建新项目 启动端口 在项目配置文件application.properties中写入 #启动端口server.port8088编写测试方法 创建控制类文件夹–>便于规范我们新建一个controller包–>建一个HelloWorld.class package com.example.hellospringboot.controller;import org.springframew…

【工具推荐】一键多平台文章发布神器推荐(免费)

hello&#xff0c;大家好&#xff0c;我是你们老朋友洛林&#xff0c;上一篇文章说到自己深受多平台手动发布的折磨「传送门」&#xff0c;准备开发一款文章多平台工具&#xff0c;后来联系到 Wechatsync 原作者进行了简单的沟通&#xff0c;下面是关于以后的一些规划&#xff…

MySQL 批量修改表的列名为小写

1、获取脚本 SELECT concat( alter table , TABLE_NAME, change column , COLUMN_NAME, , lower( COLUMN_NAME ), , COLUMN_TYPE, comment \, COLUMN_COMMENT, \; ) AS 脚本 FROM information_schema.COLUMNS WHERE TABLE_SCHEMA 数据库名 and TABLE_NAME表名-- 大写是up…

让你认识C++中的模板

目录 一. 泛型编程1、定义 二、函数模板1、定义2、格式3、函数模板的实例化&#xff08;1&#xff09;、强制转化&#xff08;2&#xff09;、显式实例化 三、类模板1、 类模板的定义格式2、实例3、 类模板的实例化 一. 泛型编程 1、定义 泛型编程&#xff1a;编写与类型无关…

Spring封装数据结果

Spring封装数据结果 POST请求JSON格式 基本数据类型 public class Demo {private byte aByte;private short aShort;private int anInt;private long aLong;private float aFloat;private double aDouble;private char aChar;private boolean aBoolean; }没有传键 封装时就会…

arm2 day4

汇编编写流水灯 代码&#xff1a; LED流水灯现象&#xff1a;

Aspose.OCR for .NET 2023Crack

Aspose.OCR for .NET 2023Crack 为.NET在图片上播放OCR使所有用户和程序员都可以从特定的图像片段中提取文本和相关的细节&#xff0c;如字体、设计以及书写位置。这一特定属性为OCR的性能及其在扫描遵循排列的记录时的功能提供了动力。OCR的库使用一条线甚至几条线来处理这些特…

思科C9300交换机堆叠

思科C9300交换机堆叠仅支持同质堆叠&#xff0c;即仅以Cisco Catalyst 9300系列交换机作为堆叠成员的Cisco Catalyst 930O系列交换机堆叠。需要专用电缆&#xff0c;除数据交接支持堆叠外&#xff0c;还支持电源的堆叠。 在所有交换机上运行命令 "switch convert mode sta…

确定性 vs 非确定性:GPT 时代的新编程范式

分享嘉宾 | 王咏刚 责编 | 梦依丹 出品 | 《新程序员》编辑部 在 ChatGPT 所引爆的新一轮编程革命中&#xff0c;自然语言取代编程语言&#xff0c;在只需编写提示词/拍照就能出程序的时代&#xff0c;未来程序员真的会被简化为提示词的编写员吗&#xff1f;通过提示词操纵 …

Python使用Numba装饰器进行加速

Python使用Numba装饰器进行加速 前言前提条件相关介绍实验环境Numba装饰器进行加速未加速的代码输出结果 numba.jit加速的代码输出结果 前言 由于本人水平有限&#xff0c;难免出现错漏&#xff0c;敬请批评改正。更多精彩内容&#xff0c;可点击进入Python日常小操作专栏、Ope…