Transcription
Hello 大家好,我是小白。这是一个理论结合实作的机器学习课程。实作的部分我们会用 Python 来完成,所以如果你还没有学过 Python 的话,可以先去看我的 Python 课程。理论的部分,因为这不是一堂数学课,所以里面不会有太艰深的数学,大家也不用担心。好,那我们就废话不多说,直接开始吧。
Hello 大家好,我是小白,非常欢迎您来到这个课程。首先先跟大家厘清几个观念。第一个,什么是人工智能 AI?AI 的全写是 Artificial Intelligence。大家应该常常听到人工智能吧?那究竟什么是人工智能呢?人工智能,或者你可以叫他人工智能,用简单的一句话来说,就是我们希望机器可以跟人类一样拥有智慧。一旦机器有了智慧,他就可以帮我们处理很多繁杂的事情啦。
好,接着,什么是机器学习 ML?ML 的全写是 Machine Learning。我们刚刚说人工智能是我们希望机器可以拥有智慧,那要怎么样让机器拥有智慧呢?其中一个方法就是透过让机器去学习。好,现在问题来了,要怎么样让机器去学习呢?我们可以先想一下,人类是怎么样学习的?人类是不是可以透过过去的历史跟过往的经验来做学习嘛?对,机器来说也是一样啊。机器的过去历史跟过往经验,其实就是过去储存下来的资料嘛。所以机器学习呢,我们用简单的一句话来说,就是从过去储存下来的资料中去找出规则。OK,机器学习用简单的一句话来说,就是从资料中去找出规则。
好,接着,什么是深度学习 DL?DL 的全写是 Deep Learning。其实深度学习呢,它就是机器学习的其中一招。我们刚刚说机器学习,他是从资料中去找出规则。那要从资料中找出规则有很多种方法,其中的一个方法呢,就是深度学习。那深度学习这个方法,他是透过模仿人类大脑的一个类神经网路,来从资料中去找出规则。OK,那至于什么是类神经网路呢?之后的课程还会再跟大家做介绍。
好,所以我们总结一下刚刚说的人工智能、机器学习还有深度学习。我们可以用这一张图来做一个表示。人工智能是我们最终想要达成的目标,我们想要机器跟人类一样拥有智慧,那他就可以帮我们处理很多繁杂的事情。那要怎么样实现人工智能呢?其中一个方法就是透过让机器去学习。那机器学习呢,用简单的一句话来说,就是从资料中去找出规则。要从资料中找出规则有很多种方法,其中一个很厉害的方法就是透过深度学习。那深度学习这个方法呢,它就是透过模仿大脑的一个类神经网路,去达成从资料中找出规则。OK,所以呢,这个是人工智能、机器学习还有深度学习,他们三者间的关系。
接下来我们来了解一下机器是如何学习的。之前已经提过了,机器学习就相当于是从资料中去找出规则。那接着我们进一步来探讨,机器或者说电脑是怎么样从资料中找出规则的呢?其实只要透过一些数学的技巧搭配程式,就可以达成让机器从资料中找出规则了。
好,那接着我们就直接来看一下,机器学习的过程大概长什么样子。假设现在我们的手上有一些图片的资料,这些图片是狗跟猫的图片。我们想要让机器从这些图片的资料中学会如何分辨狗跟猫。OK,也就是说,我们想要让机器从这些资料中里面去找出分辨狗跟猫的规则。那我们可以怎么做呢?大家可以先想一下,人类是怎么样学会分辨狗跟猫的。假设今天你要教一个小朋友,这个小朋友他完全不知道什么是猫什么是狗,你要教会他如何分辨。一开始你可能拿了一张图片,问小朋友说:“这只是猫还是狗呢?”可以看到小朋友的眼神相当的茫然,因为他跟本不知道你在说什么,他只想赶快睡觉。于是呢,他随便乱回答他说:“这只是狗。”显然的他答错了。这时候身为老师的我们呢,就给他一个叉叉,并且告诉这个小朋友长这个样子的动物是猫哦。接着我们又拿了一张图片,问小朋友:“这只是猫还是狗呢?”可以看到,相较于上一次的茫然,这一次小朋友的眼神透露出了一点点的自信,他回答说:“这只是猫。”相当的可惜,他又答错了。身为老师的我们又给他打了一个叉叉,并且告诉小朋友长这个样子的动物是狗哦。就这样经过不断的看照片然后让小朋友作答,看照片让小朋友作答,只要小朋友他答错了我们就请他修正。久而久之,小朋友看多了猫跟狗的图片,他大概也会知道猫长什么样子,狗长什么样子了。所以你再问他:“这只是猫还是狗呢?”可以看到他就非常坚定、非常自信的告诉你:“这只是狗。”
同样的,这样子的学习方式,一样可以套用到机器身上。我们可以输入一张图片到机器里面,或者说输入一张图片到电脑里面,并且问他:“这只是猫还是狗呢?”输入到电脑里面以后就会触发一些程式,那这些程式的背后其实就是在使用一些数学的技巧。这边的数学技巧搭配程式,我们也可以把它叫做模型 (model)。所以呢,这边我们可以这样子说,我们可以把一张图片输入到模型里面,并且问他:“这只是猫还是狗呢?”那一开始的时候,这个模型跟一个无知的小孩是一样的,他并不会知道什么是猫什么是狗。所以呢,他随便乱猜:“这只是狗。”显然他答错了。我们这时候就要给他一个叉叉,并且告诉他:“长这个样子的是猫哦,请他修正一下模型。”“长这个样子的是猫哦。”接着我们又拿了另外一张图片,输入到机器里面,输入到模型里面,并且问他是猫还是狗呢?这次他回答是猫,显然的又答错了。一样给他打一个叉叉,并且告诉他:“长这个样子的是狗,请他修正一下模型。”一样就经过这样子不断的训练,不断的给机器看很多猫跟狗的图片,如果他答错了就请他修正,直到最后这个模型他有了一定的正确率之后,我们就说这个模型训练完成了。可以看到最后我们输入一张狗的图片,这个模型告诉你:“这只是狗。”
那训练好机器之后,或者说训练好这个模型之后,假设未来你有一张新的图片,你想要问他是猫还是狗,你就可以把它输入到模型里面,他就会告诉你:“这只是狗。”
接着再来看一个例子。假设现在我们的手上有一些房屋买卖的资料,这些资料是房屋的坪数跟他相对应的出售价格。一样,我们想要让机器从这些资料中去找出规则,找出坪数跟他相对应的出售价格之间的规则。也就是说,我们想要由坪数去推测出售的价格应该是多少。那这样子的话,我们就可以说坪数叫做特征 (feature),价格叫做标签 (label)。我们由坪数去推测价格是多少,所以坪数叫做特征,价格叫做标签。
接着训练的过程就跟刚刚大同小异。我们可以把坪数也就是特征输入到模型里面。那模型一开始是无知的,所以呢,他会随便乱猜测。假设他猜测400万,我们就看一下我们手上的资料,我们手上的资料写说这个50坪的房子,他出售的价格是500万,也就是我们的标签是500万。那显然跟他的猜测是有一段距离的。所以这时候呢,我们就告诉这个模型:“这个房子他出售价格是500万哦,请你修正一下。”接着一样输入下一笔资料,66坪,然后到模型里面,他推测900万。我们再看一下手上的资料是650万,标签是650万。所以呢,一样再修正一下模型。就这样不断不断的修正,不断不断的看资料,直到最后模型的预测跟我们真实的资料,也就是标签误差值不大的时候,我们都说这个模型训练完成了。那模型训练完成之后,假设你有一个房子想要卖,它是72坪,你不知道他应该卖多少,你就可以把72坪这个特征输入到模型里面,让他预测一下应该可以卖多少钱。
讲到这边,你可能就有疑问啦,一栋房子的价格,应该不是单单看它有几坪就可以决定的吧?我们可能还要看它的位置,看它的格局等等的综合因素,才有办法评估一栋房子值多少钱。没错,所以你可以把资料收集的更完整一点。除了坪数以外,我们现在还收集了他的位置还有房间的数目。所以现在机器就是要由坪数、位置跟房间的数目去推测出售的价格应该是多少。那现在我们就会说坪数、位置还有房间叫做特征,价格呢一样是标签。因为我们是由坪数、位置跟房间来推测价格,所以这3个是特征,价格是标签。
学习的过程就跟刚刚一样大同小异。我们输入特征坪数、位置还有房间的数目到模型里面。那模型一开始是无知的,所以他会随便乱推测。假设一开始推测400万,我们看一下手上的资料,也就是标签,标签是500万,所以呢,请模型修正一下。接着再输入第二笔资料,一样请模型推测,他推测900万,看一下标签是650万,再请模型修正一下。就这样不断不断的训练,直到模型的误差值到了一定的范围内,我们都说这个模型训练完成了。一样,未来如果你有一间房子,它是72坪,在华盛顿,有6个房间想要卖,你想要推测他应该可以卖多少钱,就可以输入到这个模型里面,他推估说可以卖730万。
这个大概就是机器学习的过程。接下来我们就直接来实作喽。
这堂课我们要使用的是 Colab。Colab 是 Google 免费提供的 Python 撰写环境。它可以让我们在浏览器上面非常快速简单的撰写 Python 程式。但因为是 Google 提供的,所以呢,你要先办一个 Google 帐号并且登录。登录好之后呢,在这边可以看到9个点,点进来找到云端硬碟。我们在左上角可以看到一个新增,然后点选这里的更多。如果在这里你已经找得到 Google Colab 的话,就点选它。找不到我们就点选连接更多应用程式。好,我们在这里可以找到 Colab,点进来,然后点选安装。点选安装,他可能会叫你登录一下,那你就登录。好,登录好安装好之后呢,我们就点选确定完成。OK,接着我们再到新增这里更多,可以看到多了一个 Google Colab,就点进来。点进来之后可以看到,他就帮我们创建了一个可以撰写 Python 的环境。
首先呢,我们先看到左上角。左上角这边是档案的名称,你可以把它做修改。假设我把它改成“环境建置”。OK,这样就修改过来了。接着因为我个人比较习惯背景是黑色的,所以呢,我到工具这边找到设定,那这边有一个网站里面的主题,我们选到 Dark,然后点选储存。背景就会变成黑色的。当然,你也可以自己设定你喜欢的颜色。那在编辑器这边呢,你也可以设定文字的大小啊、间距等等的,就留给大家自己来玩。
那我们在撰写程式之前呢,要先做连线的动作。我们可以到右上角这边找到一个连线,点下去。那他就会分配一些资源给我们做使用。等他一下。好,连线好之后呢,我们可以看到中间这一块。中间这一块呢,就是我们可以撰写程式的地方。首先我们到这个格子里面,就可以直接输入程式码啦。假设我输入 `print(87)`。那我把它放大一点,大家看的比较清楚。好,`print(87)` 打完之后,这边有一个执行的按钮,点下去。那他就会开始执行了。执行的结果呢,就会显示在下面。
在 Colab 里面程式码是可以分成一块一块的。我们可以把滑鼠放到这个格子的上面一点点,可以看到它就跳出了一个“加程式码”或者是“加文字”。放到下面一点点,一样会跳出来。假设我点选“加程式码”,哎,它就多出了一个格子。那一样,我们就可以在这个格子里面撰写 Python 程式。假设我写 `print(88)`,按下执行,可以看到执行的结果就会显示在下面。
那我们来看一下,如果我点选“加文字”,他就跳出一个可以撰写文字的格子。假设我写一个“你好,我好,大家好”。这边除了撰写文字以外,你也可以选择一些字体啊、字形等等的。可以看到这边就多了一个文字的格子。OK,我们可以加很多程式码的格子,也可以加很多文字的格子。那如果你不想要的话,这边可以把它删掉,有一个垃圾桶的符号,点下去就删掉了。删掉,删掉,删掉。OK。
那 Colab 呢,它还有一个重点,就是它可以提供免费的 GPU 跟 TPU 给我们使用。那 GPU 跟 TPU 呢,是在运算上面可以帮我们加速很多。我们可以在上面这边找到编辑,然后有一个笔记本设定。可以看到这边有一个硬体加速,你就可以选择 GPU 或者是 TPU。那在之后的课堂呢,我们也会使用到 GPU 来做加速。所以假设这边我选择 GPU,然后按下储存。
你可以看到他就帮我们重新做连线分配。一样要等他一下。他连线好之后,会问我们说要不要删除先前的执行阶段。假设我这边就先按取消。好,连线上 GPU 之后,我们想要看一下他给我们使用的 GPU 是哪一张的话,我们可以在程式码的格子里面打上 `!nvidia-smi`。按下执行。可以看到他现在给我们使用的 GPU 是这一张特斯拉 T4。OK。
好,那这样子呢,我们的环境建置就完成啦。
第一个要跟大家介绍的数学技巧,叫做简单线性回归 (Simple Linear Regression)。可以看到它的名字前面有“简单”这两个字,所以可想而知呢,它很简单,大家不用太担心。
我们先来描述一下今天遇到的状况。假设今天你是一间新创公司的老板,你想要聘顾你的第一个员工,但是呢,你不太确定应该要给他多少薪水。于是你就到市场上去收集了一些同职位的人的资料,那这些资料是他们工作的年资跟对应的薪水。那我们把这边的这些资料呢,把它用图表画出来会长这样。X 轴是年资,Y 轴是月薪。这里的每一个叉叉,就表示你收集到的每一笔资料。那从这张图我们不难看出来,年资跟月薪是成一个正比关系的,也就是年资越高的人,月薪也会越高。
好,那现在问题来了,你是一间新创公司的老板,你想要聘顾你的第一个员工。你的第一个员工来了,他告诉你他的年资是两年半,也就是 2.5。那请问你应该开多少薪水给他呢?这时候就可以使用到简单线性回归啦。由于年资跟月薪是成一个正比的关系,所以这些资料我们大概可以使用一条直线来做表示。简单线性回归,就是把资料用一条最适合的直线来做表示。假设现在我们已经找到了这条最适合的直线,那我们就可以把 2.5 这个年资带到这条直线里面去,看一下大概可以给这位员工多少薪水。带进去之后呢,可以看到我们大概可以给这位员工 51K 的薪水。
那现在问题就会变成,要怎么样找出这条最适合的直线呢?我们先来看一下,一条直线在数学上可以怎么样做表示?我们可以写 `y = w*x + b` 来表示一条直线。那把这条式子套用到现在这个例子里面,y 就会等于月薪,x 就会等于年资。所以呢,现在的问题就变成,我们要找出一个最适合的 w 跟最适合的 b 来表示这些资料。我们要找出一个最适合的 w 跟最适合的 b 来表示这条直线。
好,那我们就直接来实作看看,不同的 w 跟不同的 b 会产生怎么样的直线呢?
首先我们先把资料读取进来。我们可以使用 Pandas 这个模组来做读取的动作。这边我把它叫做 `pd`。Pandas 这个模组是一个非常好用的资料处理工具。在 Colab 里面我们不需要特别安装它,因为预设已经安装好了,所以我们可以直接引入做使用。接着我们要使用的资料,它的网址呢,你可以在课程档案里面找到。我把它用一个变数来做表示。这边特别注意,我们要读取的这个档案,它是一个 CSV 档,所以呢,我们可以使用 Pandas 它底下的 `read_csv` 来做读取的动作。我们只要把网址写到参数里面,它就可以做读取了。这边我一样用一个变数来做表示。
我们直接把读取的结果把它显示出来看看。执行。这就是我们要使用的资料,年资跟他对应的薪水,总共有 0-32,也就是 33 笔资料。我们想要用一条直线来表示这一些资料。一条直线我们说过了,在数学里面可以写成 `y = w * x + b`。那在我们这个例子里面呢,我们是想要用年资去预测薪水,所以年资就会是 x,薪水就会是 y。我们先把 x 跟 y 把它分离出来。我们想要去取得 `years experience` 这一列。那想要去取得这一列的话,可以在后面写上中括号 `years experience`。这样他就会把那一列分离出来。x y 也是一样,我想要去取得 `salary` 这一列。那我把它显示出来看看 x 跟 y。
好,没有问题。接着呢,我们先把这些资料对应的图把它画出来看看。我在另外开一个格子。要画图的话可以使用一个非常好用的套件叫做 Matplotlib。我们使用它底下的 `pyplot`,我把它叫做 `plt`。一样,Matplotlib 这个套件在 Colab 里面预设也是安装好的,所以我们可以直接引入做使用。我可以使用它底下的 `scatter` 来把图画出来。我们只要把刚刚分离出来的 x 跟 y 把它传进去,它就会帮我们把图画出来了。然后最后我要把它显示出来,要写一个 `show`。执行。
可以看到他就把我们的资料用一个点的方式,一点一点的把它显示出来。那如果你想要改变这个点的样式,假设我想要把它改成叉叉,我想要改变它的颜色。那这边的我可以另外再加一些参数。我可以再写一个 `marker='x'`,原本的标记是圈圈嘛,我想要把它变成叉叉,那我可以写上 `x`。然后我也想要改变颜色,我可以设定 `color='Red'`,想要把它变成红色,我写 `Red`。好,执行。可以看到标记就变成红色的叉叉。至于这边还可以有什么标记,还可以是什么颜色,大家有兴趣的话就留给你自己去研究。
这边我可以再帮图加上一个标题。我可以设定它的 `title`,假设我的标题是“年资对应他的薪水”。好,我们执行。可以看到好像出了一点错误。在标题的这个部分呢,它出现了4个框框。会发生这个错误的原因是因为 Matplotlib 它预设是不支援中文的。如果我们想要显示中文的话,可以自己加入中文的字体。
好,那就来加入一下。首先我们要先去下载一个中文的字体。这边我在另外新建一格来做下载的动作。我们可以使用 `wget` 这个工具来做下载。只不过预设在 Colab 里面是没有安装它的,所以我们要先安装。想要在 Colab 里面安装套件安装模组,可以输入惊叹号后面一样 `pip install` 你想要安装的东西。安装好之后引入它。接着我们就可以使用它底下的 `download` 来做下载的动作。只要在参数里面写上你想要下载的东西,他的网址就可以了。那这个网址呢,一样在课程档案里面都可以找得到。这个就是我们要下载的字体。好,直接执行。它会先安装,接着引入下载。下载完成。
我们来看一下,在左边可以找到一个档案打开来,这就是我们刚刚下载的字体,它叫 `Chinesefont.ttf`。好,这边我先把它关起来。下载好字体之后呢,我们就可以把它加入到 Matplotlib 里面啦。要加入字体的话,我们额外再引入 Matplotlib,我把它叫做 `mpl`。然后再从 Matplotlib 它底下的 `font_manager` 引入 `fontManager`。那这边都引入完之后呢,就可以先用 `fontManager` 它底下的 `addfont` 来加入字体。我们刚刚下载的字体叫做 `Chinesefont.ttf`,所以我们就在这里写上 `Chinesefont.ttf`。加入字体之后呢,我们还要设定现在要使用这一个字体。那要设定要使用这个字体,我们可以用 `mpl` 它底下的 `rc` 来设定一些东西。我们要设定的是字体,所以第一个参数写上 `font`,第二个参数就指定要使用的字体是 `Chinesefont`。好,那这样子就应该没问题了。
我们先把字体加入进来,再设定要使用这个字体,再执行一次。可以看到现在中文就可以正常的显示了。这里除了设定图表的标题以外,我们也可以设定图表的 X 轴跟 Y 轴标签。要设定 X 轴标签的话,我们可以使用 `xlabel` 来做设定。那我们的 X 轴是年资,所以呢,我写上“年资”。再来是 Y 轴,Y 轴的话就是 `ylabel`,Y 轴是月薪,那它的单位是千,所以呢,写上“月薪 (千)”。再执行一次。可以看到现在 X 轴跟 Y 轴的标签都上去了,年资跟月薪。
接着我们来把直线把它画出来。这边我在另外新增一格。刚刚已经提到了,在数学上一条直线可以写成 `y = w * x + b`。那特别注意,我们是想要用 x 的值去预测 y,所以呢,这个 y 跟我们这个 y 是不一样的,这个 y 是预测的值,那这个 y 呢,是真实的数据。所以呢,我们回到下面,我们就可以用 x 去乘以一个 w 加上 b 来表示我们预测的 y。所以呢,我把它叫做 `y_pred`,表示预测的 y。那现在呢,假设一开始我先把 w 设定成 0,b 也设定成 0。我们先来看看 w=0, b=0 画出来的直线会长什么样子。
要画直线的话,我们可以用 `plt` 它底下的 `plot` 来画。第一个参数就写上 x 的值,第二个参数就写上 y 的值。那这次呢,我们的 y 值是 `y_pred`,预测的结果。一样要把它显示出来,要写上 `show`。OK,我们执行看看。可以看到这就是 w 等于 0,b 等于 0 的时候画出来的直线。那我也把它的颜色改一下,假设我把它的 `color` 把它设定成蓝色 `blue`。这个颜色我觉得不错。
接着呢,我把上面的这些资料,把这些叉叉也把它画进来。我就把这里的这些都复制过来,复制在这里。好,再执行一次。那这个就是我们的预测线,这些呢,是真实的资料。我们的目标呢,就是要找出一条最能表示这些资料的直线。那现在这条直线是 w=0, b=0 的结果,所以呢,我们要找出一个最适合的 w 跟 b 来表示这一些资料。
那在这之前,我们可以再帮图表加上一些图例。我们这一个 `plot` 是画直线嘛,那 `scatter` 是画这些叉叉。我们可以在它的后面再加上一个 `label`。这个图例呢,是“预测线”。然后一样在画叉叉这边呢,我们也可以再加上一个 `label`。它呢,是“真实数据”。加上这个 `label` 之后,我们可以在这边再写上 `plt.legend()`,它就会把图例显示出来。好,我们执行。可以看到这边图例就显示出来了,这条直线呢,是预测线,然后这些叉叉是真实数据。
我们先把这边的这些呢,把它写成一个函式,这样子呢,好让我们带入不同的 w 跟 b。我把它叫做 `plot_pred`,然后它可以传入 w 跟 b。把这些把它做缩排。我们就来试试看带入不同的 w 跟 b,画出来的结果会长怎么样。这边我们就可以呼叫 `plot_pred`。假设我一样先带入 0 跟 0。执行一次。可以看到跟刚刚的结果是一样的。假设我把 b 的值做修改,我把它改成 10。再执行一次。可以看到这个线呢,看起来好像没有变,但实际上是有变的。是因为我们现在 X 轴跟 Y 轴的这个值呢,X 轴跟 Y 轴的值我们没有固定,所以图会看起来好像没有变。所以这边我们先把 X 轴跟 Y 轴的值做一个固定。要固定的话呢,这边我们可以这样子写 `plt.xlim()`,我们设定它的最大跟最小的范围。X 轴的话是 0-10 左右嘛,这边还有多一点,所以我把它设定 0-12。这边我们可以这样子写哦,在里面写上一个这个列表,然后呢,设定它的最小跟最大值。然后 Y 的值呢,也是一样,我们设定它的最大跟最小。这边的话,因为有可能到负数,所以呢,我让他最小是 -60 好了,最大的话就让他到到 140。好,那我们再执行一次。可以看到现在呢,X 轴跟 Y 轴的值就会是固定的。再执行一次,X 轴跟 Y 轴的值就会是固定的。Y 轴呢,是 -60 到 140。那 X 轴是 0 到 12。
我们再试一次 0 跟 0 的结果。0 跟 0 的结果,这一条直线是在 Y 等于 0 的这个地方。那如果我把 b 的值把它带入改成 10 再执行一次。可以看到这条直线呢,它就往上了。所以呢,由此可知 b 的值是控制这条直线它的上或下。如果把它改成 40,它就更往上了。那如果是 -30 呢?执行。可以看到它就往下。
那现在我们试试看改动 w 的值。本来是 0 嘛,我把它改成 10 看看。可以看到这条线呢,它变斜了,往上斜上去了。那如果改成 20 呢?就会更斜。如果我改成负数 -10。可以看到它就会往下掉下去,往下斜。正数的话就是往上斜,负数的话就是往下斜。这边我试试看 -5。好,可以看到它是这样子的一条线。那这边因为已经超出边界了,所以我们看不到。
接着我们来把这张图变成动态的。这样就可以不用一直手动调整 w 跟 b 的值。想要在 Colab 里面新增一些互动元件的话,我们可以使用 `ipywidgets` 这个工具。这里我引入它里面的 `interact`。好,那我就可以这样做使用。在第一个参数写上函式的名称,然后我们想要动态调整的是 w 跟 b 的值嘛,也就是这个函式要传入的两个参数。这边我就可以设定 w 的值,我希望它的范围假设是 -100 到 100 之间,然后它的间距是 1。再来设定 b 的值,一样假设我希望它是 -100 然后到 100 之间,一样是间距 1。我们直接执行看看。可以看到这边就会多了互动元件。预设值呢,w 跟 b 都是 0。我可以做调整,我把 w 往上加 8 的时候呢,这个直线会长这样。然后把 b 往上加。好,它会慢慢往上移动,接着再加。大家都可以自己去玩玩看,调整不同的 w 跟不同 b 的值,看一下这条直线会长什么样子。好,那就留给大家自己去玩啦。
在看完了不同的 w 跟 b 会产生什么样子的直线之后,接着我们的问题就会变成,怎么样是最适合这些资料的直线呢?我们总要定义一下吧?总要帮每一条线评个分数吧?
我们先来看一组相对简单的资料。如果我一样是想要用一条直线来表示这些资料的话,假设今天我是用这条直线,那这条直线它是 w 等于 0,b 等于 0 的结果。那我们总要帮这条直线跟这些资料的适合程度评个分数吧?或者你可以说,把这条直线跟这些资料的吻合程度评个分数吧?那我们要怎么样帮这条直线评个分数呢?非常的简单,我们只要去计算这些真实的资料跟这条线的距离加总是多少就可以了。因为如果这些资料跟这条线越吻合的话,那这些资料跟这条线的距离就会越小嘛。所以呢,我们只要把每一条线跟这些真实资料的距离计算出来,接着再从中找出最小的那一个,那一条线就会是最适合这些资料的直线。
我们直接来实际操作一次。以现在这个例子来说,总共有三个资料点,分别在 (1,1), (2,2) 跟 (3,3) 的位置。我们想要用这一条直线来表示这些资料,这条直线是 w 等于 0,b 等于 0 的结果。然后我们想要帮这条直线跟这些资料的适合程度去评一个分数。要怎么评分呢?我们可以用这些资料跟这条直线的距离加总,去做评分的依据。这个点的话,1-0,所以它跟线的距离是 1。那这个点的话,2-0,距离是 2。这边 3-0 是 3。所以呢,我们的式子会写成这样。大家可能会看到这边我除了 1-0 之外,我还把它做了平方。这边会做平方的原因,是因为它可以方便我们计算。因为在往后呢,你可能会出现负数,那直接做平方就可以把负数的问题解决了。所以以这个例子来说的话,(1-0)^2 + (2-0)^2 + (3-0)^2。最后距离的加总,应该说距离平方的加总就会是 14。
我们再看另外一条线。假设我们想要用这一条线来表示这些资料,那这条线是 w 等于 2,b 等于 0 的结果。我们一样计算他们的距离。那这边的这个点呢,它一样是 1 嘛,所以这边就是 1 减掉 2。 (1-2)^2。然后这边是 2 减掉 4。 (2-4)^2。3 减掉 6。 (3-6)^2。可以看到这边就会出现负数了嘛,如果我们没有平方的话,这边就会出现负数。所以这边一样计算完之后做加总,一样是 14。
好,再看下一条线。可以看到这条线相当的吻合,它是 w 等于 1,b 等于 0 的结果。那一样计算距离,可以发现每一个呢,都是一样的值,所以计算出来距离就会是 0。以这三条线来说的话,我们就会说这一条线 w 等于 1,b 等于 0 的这条线,是最适合这些资料的直线。
接着根据不同的 w 或 b,产生出来的分数,或者说产生出来的距离平方加总,我们可以把它写成一个函数。这个函数就叫做 cost function,中文的话是成本函数。以我们这个例子来说,cost function 可以写成这个样子。它是拿真实的数据去减掉预测值,然后做平方。也就是我们去计算真实的数据跟那条直线的距离平方,那就会是 cost,也就是我们刚刚所说的评分。
好,回到原本的例子。我们想要找出一条最适合这些资料的直线。那我们就需要帮直线评个分数,也就是计算这边的 cost。我们先假设现在所有的直线,它的 b 都等于 0 的情况下,我们去看看不同的 w 对应的 cost 会是多少。我们就一个一个点的带入。先带入一个 w 等于 -10 几,然后它的 cost 会在这里。接着带入第二个,第三个,一路带入带入带入。最后我们带入很多很多个点之后,我们会发现其实这个 cost function,也就是成本函数,它会是一个这样子的抛物线。这是 b 等于 0 的情况。
那如果 b 不等于 0,w 跟 b 所对应的 cost 又会长什么样子呢?我们来看一下。这边我用不同视角的 3D 图来给大家看。这里是 w 的值,这里是 b 的值,然后 Z 轴呢,是 cost 的值。可以看到它的图形会长这个样子。这个红点呢,是 cost 最低的点。我们再看另外两个不同的角度。好,从这两个角度我们可以看得到,这个红点的地方,我刚刚说了是 cost 最低的地方。我们可以看到,大概在 w 等于 10 几的地方,然后 b 等于 20 几的地方,会产生一个 cost 最低的点。那这也就是我们的目标嘛。我们的目标就是要找出一条最适合的直线,找出一条最适合的直线,就是找出一个最适合的 w 跟 b。最适合的 w 跟 b 所产生出来的那一条直线,跟所有真实资料的距离平方加总就会是最小的,也就是这边的 cost 最小的地方。
这边都理解了之后,接着我们就直接来实作 cost function。这边我另外开了一个新的 Colab 档案来做 cost function 的实作。一开始呢,一样要先读取资料。读取资料的做法跟之前都是一样的,所以我直接复制。好,贴上执行。资料读取好之后呢,接着我们就来实作 cost function。
再开一格。我们的 cost function 是拿真实资料去减掉预测值的平方嘛。所以呢,这边我们可以这样子写。先把预测值把它算出来,我叫它 `y_pred`。它会等于 w 去乘以 x 加上 b 嘛。那这边的 w 跟 b 现在都还不知道,所以 I 先假设 w 等于 10,然后 b 等于 0 好了。计算出预测值之后,我们就可以拿真实资料,真实资料就是 y 去减掉预测值 `y_pred`,然后我们要做它的平方。所以 I 把它括号起来然后做平方。这边呢,就叫做 `cost`。好,那我们直接把这个 `cost` 把它显示出来看看。
可以看到总共显示出了 33 个值。那这 33 个值呢,就是真实资料去减掉预测值,然后做平方的结果,也就是我们的距离平方。如果我们想要把这些距离平方的值做加总的话,只要在后面写上 `.sum()`。好,把它显示出来看看。可以看到这就是加总的结果,距离平方的加总。
如果觉得这边的值太大的话,通常我们还会把它做一个平均。我们总共有 33 笔资料嘛,所以我们这边就会除以 33。但是呢,这边这样子写比较好,我们去算这个 x 的长度。那 x 的长度就是 33 嘛,所以呢,这边除以 33。这个就是距离平方的平均。
接着我们来把这边计算 cost 的动作写成一个函式,这样好让我们带入不同的 w 跟 b 的值。好,再开一格。这边我就把它叫做 `compute_cost`。然后它要传入我们的资料 x 跟 y 以及预测的线它的 w 跟 b 的值。计算的方式呢,都跟这边是一样的。我直接贴过来。我们要先去做预测值嘛,预测值就是 w 乘以 x 加上 b。然后呢,去计算 cost,再做加总之后取平均。我就让最后 cost 的值等于加总之后再取平均的结果。最后呢,我们再把这个 cost 把它做回传。
我们来试试看带入不同的 w 跟 b,会产生什么样子的结果。这边就来呼叫看看。x y 就是这边的真实资料,然后 w,我们刚刚是带 10 跟 0 嘛,w 跟 b。一样我试试看 10 跟 0。执行。他说 `compute_cost is not define`。哦,这边还没执行。要先执行再执行这里。他回传的值就是 602 点多少。跟刚刚是一样的。如果这边我带入的是 10。再执行一次。可以看到这次的 cost 就是 227 点多少多少。那大家可以自己去带入,看看不同的 w 跟 b 产生的 cost 会是多少。
接着我们来试试看,在固定 b 等于 0 的情况下,然后让 w 等于 -100 到 100 之间,然后我们来看看它的 cost 会是多少。这边我先定义一个 cost,让它是一个列表,然后来存放 w 从 -100 到 100 之间 cost 的值。好,那这里呢,我们就可以用一个 for 回圈去做计算。我让 w 让它是从 range -100 然后到 100 之间。这边我要写 101,它就会产生一个类似从 -100 到 100 之间的一个列表。然后我们就要去计算 cost。这边的函式直接拿来做使用。x y,然后固定 b 等于 0 的情况下,让他一直带入 w 的值。这边就叫做 cost,然后呢,再把它 append 到我们的 costs 这个列表里面。最后我把它显示出来看看。执行。这边呢,这一些总共从 w 等于.. 哇,非常的多。从 w 等于 -100 到 100 之间总共有 201 个值,它的 cost。我先把它关掉。
接着我们来把不同的 w 对应的 cost 把它画成一张图来看看。这边就引入我们的绘图工具 Matplotlib 它底下的 `pyplot`,我把它叫做 `plt`。接着我们就可以使用 `scatter` 来把每一个资料点把它画出来。我们把每一个 w 跟对应的 cost 把它画出来。那我们这边的 w 呢,就是从 -100 到 100 之间,跟它对应的 cost。把它显示出来。执行。可以看到从 -100 到 100 之间总共会有 201 个点嘛。那 201 个点呢,画出来就长这样密密麻麻的。这边我们除了这样画以外,也可以直接把它连成一条线,把它连成一个抛物线。这里就使用 `plot`,一样传入 w 跟对应的 cost。再执行。这条抛物线就做连线了。
这里我边帮他加个标题还有标签。这一个呢,是 cost function 在 b 等于 0,然后 w 从 -100 到 100 之间。再设定他的 `xlabel`,`xlabel` 就是 w 的值。然后 `ylabel`,cost。执行看看。可以看到标题还有 X 轴 Y 轴的标签都上来了。
接着我们把 b 的值也考虑进来,让 b 的值也是从 -100 到 100。我们来看看 cost 会是多少。这边我引入一个在做矩阵运算非常好用的工具 NumPy,我把它叫做 `np`。现在 w 跟 b 都是 -100 到 100 之间。我们可以用 `np` 底下的 `arange`。那这个 `arange` 的用法,基本上跟我们上面的这个 `range` 是差不多的。我想要创建一个 -100 到 100 之间的一个矩阵,然后间隔是 1。我们可以这样子写。那这边我就把它叫做 `ws`,因为有很多个 w。b 也是一样,我把它叫做 `bs`。接着我再创建一个二维的矩阵。这边我可以用 `np.zeros` 来创建一个里面全部都是 0 的矩阵。第一个维度让它是 201,第二个也是 201。因为我们现在这边 w 的值总共有 201 个,b 的值也是 201 个。这个矩阵就是要存放不同的 w 跟不同的 b,它们对应到的 cost。所以 I 把它叫做 `costs`。
接着就可以来做计算了。我先用一个 for 回圈去跑过所有的 `ws` 里面的值。接着再用一个 for 回圈去跑过所有的 `bs` 里面的值。然后我们就可以计算他们的 cost。要计算 cost 的话,我们上面已经写好函式了,直接拿下来用。在这边直接复制下来。好,那 x y 是真实资料,然后这边把 w b 传进来就会计算出 cost。我们就可以把它存放到 `costs` 这个矩阵里面。这边的话,我们在另外设定义个 i,一开始等于 0。然后这里也设定义个 j 等于 0。那这里 i 跟 j 的值呢,就会等于这个 cost。然后这边让 j 让它加 1。最后这边呢,i 让它加 1。這樣子计算完之后,就会把所有的 w 跟所有的 b,它们的组合所对应的 cost,把它储存起来了。储存在这个 `costs` 里面。把它显示出来看看。执行。他说 NumPy has no attribute。这边多写了一个 r。好,再执行一次。他需要一点时间,等他一下。长这个样子是一个二维的矩阵。那里面的每一个值呢,就代表了一个相对应的 w 跟 b 所计算出来的 cost。
再來,我們就來把同時考慮 w 跟 b 所計算出來的 cost,它的圖把它畫出來看看。因為要同時考慮到 w 跟 b 的值,所以我們會畫一個 3D 的圖。要創建一個 3D 的圖,我們可以寫 `plt.axes()`,然後設定它的 `projection='3d'`。我把它叫做 `ax`。然後我們現把它顯示出來看看。執行。可以看到就創建了一個 3D 圖,不過現在裡面什麼東西都沒有。
那我們可以看得到這個圖的邊邊是有点灰色的感觉。我不想他是灰色的,我也可以做一个设定。我可以用 `ax.xaxis.set_pane_color()` 来设定颜色。我想要它是白色的,我这边可以写一个 RGB 的值,白色的就让它是 0 0 0。好,执行看看。他说没有 attribute。这边写反了。好,可以看到这个 X 轴这一面呢,就会变成白色的。那同样的我把它用到 Y 轴跟 Z 轴。这边就直接复制。只要把这里改成 Y 跟 Z。好,再执行一次。这样就会都是白色的,看起来舒服许多。
再來,我們把 w 跟 b 所對應的 cost,把它畫成一個曲面圖。這邊我們可以用它底下的 `plot_surface()`。然後就可以把我們剛剛所創建的 w 值跟 b 值,也就是 `ws` 跟 `bs` 這兩個一維矩陣把它傳進來。最後是我們儲存的 cost。好,那這邊特別注意,其實呢,我們不是單單傳入 `ws` 跟 `bs` 這兩個一維矩陣就可以了。他要的是這兩個一維矩陣所產生的一個二維網格。那我們要產生這個二維網格的話,我們可以使⽤ NumPy 它底下的 `meshgrid()`。然後呢,這邊特別注意,第一個我們先傳入 `bs`,第二個再傳入 `ws`。那他就會把它做成一個二維的網格回傳給我們。我就把它叫做 `b_grid` `w_grid`。大家想要詳細了解這個二維網格是什麼東西,他是怎麼運作的話,可以看一下這個網址。他解釋的非常詳細。這邊我就不贅述了,大家如果感興趣的話,我把網址放在下方,可以自己去研究一下。這邊我們就要改成傳入 `w_grid` 跟 `b_grid`。好,這樣沒問題。我們執行。
可以看到這個曲面圖就顯示出來了。我們再幫這個圖增加一些標題還有標籤。這邊的話,我們可以寫 `ax.set_title()`。這邊特別注意,我們前面要多加一個 `set`。跟前面不一樣,前面是直接寫 `title` 就可以了。這邊呢,要特別多加一個 `set`。`title` 的話,我們就讓它是“wb 所產生的 cost”。那就是對應的 cost。這邊會使用到中文。使用到中文的話,一樣我們就要把中文字體加入。所以呢,我一樣回到之前的這個檔案裡面。中文加入是在這個地方。前面還要下載嘛。這邊先做下載。我直接貼過來。這邊是下載好,先執行它。接著呢,就是把它加入進來。這邊是加入進來。好,我把它貼到這裡。先做下載,然後再把字體加入。這邊 title 設定好之後,接著我們設定 X Y 跟 Z 軸的標籤。那這邊一樣要加上 `set`。`xlabel`,X 軸的標籤呢,就是 w。然後再設定它的.. 這邊我直接用複製。設定它的 Y 軸標籤,Y 軸標籤就是 b。然後再是 Z 軸,Z 軸就是 cost。好,那我們把它再把它顯示出來一次。可以看到這個就是 w,然後 b,然後他們對應的 cost 是 Z 軸。
這邊如果我們想要把這個圖旋轉一下也是可以的。如果想要旋轉的話,可以在這邊設定一個 `view_init()`。它可以傳入兩個參數。第一個參數是上下的旋轉角度,第二個參數是左右的旋轉角度。假設第一個我寫 45,第二個我寫 -120。再執行一次。可以看到現在圖呢,它就轉成這樣。這邊是 w,這邊是 b,然後呢,這個是 cost。好,那這個旋轉的角度,大家也可以自己去玩玩看。
接着我觉得这个曲面的颜色不太好看,我想要把它做个修改。那可以在这里再多设定一个参数叫做 `cmap`,我把它设定成 `spectral_r`。好,我们执行看看。可以看到现在颜色就好看的许多。至于这边还可以设定成什么其他的颜色,就留给大家自己去研究哦。我觉得这个颜色不错。然后我们还可以再多设定一个不透明度的值。那不透明度呢,叫做 `alpha`。`alpha` 我让他等于 0.7。好,我们执行看看。可以看到现在他就多了一个透明的感觉,看起来就更舒服了。
接着如果我们想要让这个图更好看的话,我还可以再帮他加一个边框。我可以写 `plot_wireframe()`。然后一样传入这前三个参数。接着我设定边框的颜色是黑色。好,我们执行看看。可以看到现在多加了这个边框。但是呢,有点太黑了。一样我们可以设定它的透明度 `alpha`。假设我让它的透明度是 0.1 就好了。再执行。可以看到这样就非常的舒服,非常的好看。
接着我们来把 cost 最低的那一个点把它找出来。要找出 cost 最低的那一个点,我们可以用 NumPy 它底下的 `min()`。然后呢,去找我们整个 cost。我把它印出来看看。最低的 cost 是多少呢?最低的 cost 是 32.69 多。那如果我们想要知道 32.69 多这个 cost,它对应的 w 跟 b 是多少的话,我们可以这样子做。可以用 `np.where()` 去找出它的位置。找出所有的 cost 当中等于最低的这个 cost,它的位置它的索引在哪里。那因为这个 `costs` 它是一个二维的矩阵,所以它会回传两个值,也就是两个索引。我把它叫做 `w_index` 跟 `b_index`。好,那我们就可以把这两个值把它印出来看看。执行。可以看到它找出来的索引在 109 跟 129 的地方。我们还要从所有 w 的矩阵中去找出这个索引所对应的 w 值,b 的部分也是一样。好,那我们再执行一次。可以看到对应的值呢,对应的 w 值是 9,然后 b 是 29。也就是说,当 w 等于 9,b 等于 29 的时候,会有最小的 cost。那这边我们就可以这样子写。当 w 等于这个值,并且 b 等于这个值的时候,会有最小 cost。那最小的 cost 呢,就可以从 `costs` 这一个二维矩阵中去取值。要取的值就是这两个索引所对应的那一个值。好,我们再执行一次。他就说啦,当 w 等于 9,b 等于 29 的时候,会有最小 cost。那最小的 cost 是 36.69 多。最后呢,我们还可以把这个最小 cost 的那一个点把它画出来。那要把它画出来的话,我们就可以用 `scatter()`。第一个值呢,就传入 w 的值,然后再是 b 的值,再是 Z 轴,也就是我们 cost 的值。好,执行。可以看到最小的 cost 的那个点呢,就在这里。那我可以把它的颜色改一下,设定它的 `color` 等于红色。然后呢,我想要让它大一点,设定它的 `s` 就是大小,我让它是 40。好,再执行一次。可以看到这样就舒服了许多。
最后的最后,我想要再把这张图调整的大一点。那我可以到最上面这边来设定,我可以写plt.figure,然后设定它的figsize。它可以设定两个值,这两个值呢,分别代表图的宽度跟高度。假设我一开始是5跟5好了,看看他会长什么样子。感觉还是有一点小,我再把它变大一点,7跟7,来看看。这样看起来舒服许多了。好,那这边其他的参数,像是旋转角度啊,图的大小或是颜色等等的,大家都可以自己去做调整。
那这就是我们cost function的实作。在看完了cost function之后,接着我们的问题就会变成,怎么样有效率的去找出最佳的w跟b呢?最佳的w跟b,也就是对应到cost最低的那一个点。从我们上一个实作不难看出来,我们是用一个暴力破解的方法。我穷举了w从-100到100之间的所有值,然后去看它的cost是多少,再从中找出最低的那一个点。这边考虑的w跟b也是一样,我穷举了w跟b,从-100到100之间的所有组合它对应的cost,然后再从中找出它的最低点。但是呢,这不是一个好的办法,我们必须要有效率的找出最佳的w跟b。
那有效率的找出w跟b,这边可以使用一个方法,叫做gradient descent,中文是梯度下降。那大家也不用把它想的太困难,其实梯度下降呢,就是根据斜率去改变参数。以我们这个例子来说,参数就是w跟b,所以呢,就是根据斜率去改变w跟b的值。我们就直接来看看gradient descent是怎么样运作的。以这个当做例子,我们先假设b等于0,只考虑w的情况下,要怎么样找出一个最佳的w,它可以让cost的值最低呢?首先我们要先去设定一个初始的w值,那这个初始的w值可以随机设定,假设我设定在这里,大概等于-75的地方。接着我们可以透过微分的方式,计算出这一点的切线斜率。我们可以透过微分的方式,计算出这一点的切线斜率。
那这边大家要特别注意,当我们在使用gradient descent的时候,其实我们是不知道这条蓝色的线的。我们是不知道这条蓝色的抛物线的。这条抛物线是经过我们暴力穷举才得到的,所以呢,你并不会知道最低点在这里。
那我们重新描述一下现在这个问题,大概可以比喻成这个样子。今天你被蒙着眼睛丢到一个地方,在这个地方你只能往前走或往后走,那你的目标呢,是要走到最低点的地方。不过幸运的是,你可以透过某种方法,去计算出你目前所处的位置它前后的陡峭程度。接着呢,你就可以根据这个陡峭程度,去找出往下走的路。
回到原本的例子一样,在这个点的时候,你可以透过微分的方式,去计算出他的切线斜率。这个切线斜率也就相当于是陡峭程度。然后呢,我们就可以根据这个斜率,去找出往下走的路。
好,这边我简单的带一下,这个斜率是怎么样算出来的。首先呢,我们要先设定好cost function长什么样子。那以我们这个例子,cost function长这样,我们是拿真实数据去减掉预测值然后做平方。那以数学来表示的话,就是拿y去减掉ypred,然后做平方。这个ypred呢,又可以表示成w乘以x加上b,因为我们是想要以一条直线来表示资料嘛,所以这边是w乘以x加上b。接着在这个例子里面,b是等于0的,所以呢,我们可以直接把它省略掉。然后呢,我们只要把它对w做微分,就可以得到切线斜率了。那做微分之后会长这样。详细的微分过程我就不做给大家看了,大家有兴趣的话可以自己去研究。那实际上如果你完全不知道什么是微分也没关系,因为有很多工具都可以帮我们自动的做计算。
好,这边微分完之后,如果我们想要知道w等于-75的时候,它的切线斜率会是多少,那我们就把-75带进来。这里的x跟y呢,都是我们的真实资料,一样带进来就可以算出来切线斜率是多少了。
在我们知道了斜率之后,就相当于是知道了这里的陡峭程度。那知道了这里的陡峭程度之后,我们就可以往下走啦。要怎么往下走呢?我们可以让w去减掉斜率去乘以一个学习率。那这个学习率是什么我等等再跟大家解释。我们先看前面的w去减掉斜率。以这个例子来说,现在w大概等于-75,然后呢,切线斜率很明显的是一个负数。所以我们让w去减掉一个负数,也就是会加上一个值嘛,所以呢,加上一个值之后他就会往前了。
接着我们就一直重复这样子的动作,再去计算这一点的切线斜率。那一样,我们把数字带进来就可以找到斜率了。找到斜率之后再带到下面这一条式子。一样,现在呢,w大概等于-60的地方,显然可以看得到这个斜率还是负的。我们再让w去减掉一个负数,那就是加上一个值嘛,所以它就会往前。我们就一直不断重复这样子的动作,计算斜率更新w,计算斜率更新w。我们来看一看,计算斜率更新w,计算斜率更新。然后再来一直不断重复,直到最后我们快要接近最低点或是在最低点的时候。
这边的切线斜率就会相当接近0。OK,这边的切线斜率就会相当接近0。那相当接近0之后,我们让w去减掉一个接近0的值,也就相当于w是没有更新,那我们就找到最低点啦。这大概就是gradient descent的运作过程。
接着呢,我们来看一下这个学习率是什么东西。学习率它的英文是learning rate。那其实讲简单一点呢,学习率就是我们往下走的步伐大小。好,回到刚刚的例子,一样一开始我们先找一个初始的w,然后呢,就可以计算它的切线斜率。计算完切线斜率之后要往下的话,我们可以让w的值去减掉斜率乘以一个学习率。那这个学习率呢,就是你要设定一个数值,这个学习率是多少你要自己去决定。我们来看一下这里是拿斜率去乘以学习率。如果你的学习率越大的话,这边就会越大嘛,有可能是正的越大,也有可能是负的越大。那我们拿w去减掉一个越大的值,w的变化就会越大,也就是说它的步伐会越大。反过来,如果你的学习率越小的话,这边相乘之后就会比较小,然后我们拿w去减掉一个比较小的值,w的变化就会比较小,也就是步伐会比较小。
好,那我们就来看一下,不同的学习率会有什么样子的结果。先来看学习率如果是大的话,那他的步伐就会相对比较大。OK,比较大。好,看到这边你可能会有一个疑问,就是他的步伐是不是渐渐在变小?没错,他是渐渐在变小。即便我们的学习率都是保持一样的,他的步伐也会慢慢变小。为什么呢?因为这边的斜率也会变。他在这边的斜率是比较大的,然后呢,越靠近最低点的地方斜率就会越小,所以他这里呢,步伐也会变小。
接着我们来看,如果学习率是比较小的话会长什么样子呢?那他的步伐呢,就会比较小。我把两张图放在一起比较一下会比较清楚。左边呢是学习率大的,右边呢是学习率小的。可以看到左边他的步伐就比较大,右边呢就比较小。
讲到这边你可能就会说,那我们肯定是把学习率设定成很大啊,因为设定成很大,他往下走的速度才会快嘛,这样我们才可以越快到达最低点。这是一个非常好的问题,但是呢,我们的学习率也有可能会过大。过大的话会长什么样子呢?我们来看一下。你第一步就走到这里了,接着第二步,你不是走到最低点哦,你是直接跨过去,跨到了对面。那我们再来看第三步呢?第三步他又跨回来了。你就会这样一直不断的跨过去又跨过来,跨过去又跨过来,永远都没有办法走到最低点,因为你的步伐太大了,根本没有办法走到最低点。这是学习率过大的问题。
我们再来看一下,如果学习率过小的话会怎么样呢?你的每一步都很小很小很小很小,很小到你走到天荒地老也一样没有办法走到最低点。所以呢,学习率你不能太大也不能太小,要找到一个最适中的值。那要怎么样找到最适中的值呢?我们可以透过不断的实验跟测试去找到它。
好,那这就是学习率。接着我们再回到原本的gradient descent。刚刚的例子都是只有在考虑w的情况下。如果我们现在连b也要考虑的话,那这个gradient descent会怎么运作呢?基本上都是一样的。首先呢,你要先去设定一个随机的初始w跟b的值。那这边你看到的这个图呢,也是我们经过穷举之后才得到的,所以呢,你并不会知道这个最低点在这里。
那我们就直接套用到比喻里面。今天呢,你一样莫名其妙的被带到了一个地方,然后蒙上了眼罩。这个地方呢,有点类似一个峡谷的地形。那你的目标一样是要找到这个峡谷的最低点。不过这次跟上次不一样的是,你除了可以前后走以外,你也可以左右走。你一样可以透过某种方法得知你前后的陡峭程度,还有左右的陡峭程度。那你可以透过陡峭程度去找到往下走的路了嘛。
这边跟刚刚一样,陡峭程度就代表斜率。所以呢,这里就是计算w方向的斜率。那这边呢,就是计算b方向的斜率。要计算斜率的话,一样我们可以对cost function做微分。以这个例子来说,cost function长这样,是用真实数据去减掉预测值然后做平方,也就是y减掉ypred然后做平方。其中这边的ypred呢,我们还可以拆解成w乘以x加上b。接着对w作微分就可以得到w方向的斜率。那如果对b作微分呢,就可以得到b方向的斜率。
做完微分之后结果会是这样。w方向的斜率长这个样子,b方向的斜率长这个样子。一样,如果我们想要知道这个点它的斜率是多少,那我们就把数值带进去就可以了。把这边的w带进去,把这边的b带进去,然后x跟y呢,也是我们有的数据了,那就可以计算出这边的w方向斜率跟b方向斜率。
接着呢,我们就可以来更新w跟b的值啦,也就是我们可以往下走了。要更新w,我们就拿w去减掉w方向的斜率去乘以学习率。那要更新b呢,就是拿b去减掉b方向的斜率乘以学习率。這樣子呢,就可以往下走了。到这个点之后,一样我们再重新计算一次w方向的斜率跟b方向的斜率。计算完之后呢,就可以做更新。那我们就可以慢慢的往前走,往前走走到最低点的地方。
那我们是蒙着眼睛在走,要怎么样判断自己是不是在最低点的呢?一样,当你越靠近最低点的时候,w方向跟b方向的斜率就会越小。然后你又让w跟b去减掉一个很小的值,那它就相当于没有怎么样变,我们就可以借此来判断现在自己的位置大概在哪里。
学习率的部分,一样你要自己去设定一个值。这个值不能太大也不能太小。如果你的学习率设定的过大的话,那他的步伐就会非常的大,大到有可能永远没有办法到最低点的地方。反之,学习率如果过小的话,你的步伐就会非常的小,小到你走到天荒地老可能也走不到最低点的地方。
这就是我们的gradient descent,它的运作过程。接着我们就直接来试做看看啦。
好,那我们就来实作gradient descent啦。首先一样要先读取资料。读取资料的动作跟之前都是一样的,所以我直接用复制的。接着我们刚刚说了,gradient descent就是去计算斜率然后更新参数。要计算w方向的斜率,我们可以把cost function对w作微分。反正要计算b方向的斜率,我们可以把cost function对b作微分。我们就来计算一下。
我们把cost function对w作微分,它的结果是这样2乘以x,然后再乘以w乘以x加上b去减掉y。这个呢,是把cost function对w作微分的结果。那如果我们把cost function对b作微分的话,其实呢,差别就只是这边少乘了一个x。那w方向的斜率我就叫它w gradient,那b方向的呢,我就叫它b gradient。
现在如果我想要知道,当w等于10,b也等于10的时候,它w方向跟b方向的斜率是多少呢?那我就先设定w等于10,b也等于10。我们先来看w方向的好执行。我们可以看到它总共产生了33个值,因为我们总共有33笔资料嘛,你每一笔资料带进去都会产生一个斜率。这边我们就把它做平均。要做平均的话,我们可以先把它做加总的动作,然后再去除以它有几笔资料。那这边呢,有几笔资料我用n来表示,我们去计算x的长度,然后这边就可以除以n。我们再执行一次看看。这个就是平均的结果是-118点多少多少。
那我们再看一下b方向的。一样我们有33笔资料,所以呢,他会计算出33个值。这边我们的做法一样,就是把它做加总之后再除以n。再执行。可以看到平均的结果呢,就是-27.46。那其实这边如果我们想要计算平均的话,也可以直接这样子写。我们写mean,他一样是计算平均。这边就不需要在另外去计算x的长度了。可以看到执行的结果是一样的。这边w方向也改一下。好,都是一样的。
那为了方便,我就把计算斜率或者你可以说计算梯度的这个动作,把它写成一个函式。这边呢,我就叫它compute gradient。我们要传入x跟y也就是我们的资料,然后跟wb的值就可以做计算了。我们就把计算的结果把它做回传,w方向以及b方向。
那我再开一格来试试看。假设我现在想要知道的是,w等于20,然后b等于10的时候,它计算出来的斜率会是多少?这边要先执行再执行它。w方向是537点多少,然后b方向是70点多。
计算出w跟b方向的斜率之后,接着我们就可以去更新w还有b的嘛。因为我们的更新方法,就是拿w还有b去减掉它的斜率乘以一个学习率。我们先假设初始的w是0,那初始的b也是0。这边你可以随便乱设,我就把它设定成0。然后呢,我们就可以去计算当w等于0,b等于0的时候,它的斜率是多少。接着我们就可以根据这个斜率,去更新w,还有b的值。
要计算斜率就使用刚刚写好的函式,我们把wb带进来,它就会回传给我们w还有b方向的斜率。那我们就可以根据这个斜率去更新w,还有b的值。要更新w的话,我们可以把w去减掉w方向的斜率,然后乘以一个学习率。这边学习率经过我的测试跟实验之后呢,我觉得0.001不错。好,那要更新b的话,一样就是把b减掉b方向的斜率,然后乘以这个学习率。我就让w等于更新之后的结果,然后b也等于更新之后的结果。我们把它显示出来看看。执行。
可以看到更新之后呢,w从0变成了0.87多,然后b从0变成了0.14多。那接着我们就来看看wb从0,0变成這樣子,是不是真的有让cost下降,也就是它是不是真的有往下走。要计算cost的话之前也写好了,就直接复制过来。这个compute cost直接复制过来。那这边呢,我们就可以直接拿来做使用。我们就来看看wb本来从0,0,然后呢,变成这个新的值之后是不是真的cost有下降。我把它印出来,把它印出来执行。
可以看到本来是6,040然后变成5,286,所以真的cost是有在下降的,也就是我们真的有在往下走。
好,讲到这边我们先暂停一下。我们先看一下刚刚计算梯度的地方是怎么写的。找到compute gradient。在这边,不管是在计算w方向还是b方向的梯度,大家应该会发现它都有一个乘以2。那其实这个乘以2是可以省略掉的。这边我们是可以把它省略掉的。好,省略完之后我再执行一次。为什么可以省略掉呢?我们再往下看这边,我们是不是拿w去减掉w方向的斜率乘以一个学习率,然后b去减掉b方向的斜率乘以一个学习率。我们刚刚w跟b方向的斜率都有乘以一个2,就相当于我们有在后面再乘以一个2。那其实这个乘以2是没有必要的,因为呢,它会间接的影响到步伐的大小。步伐的大小我们就由learning rate来控制就好了。所以这边乘以2呢,其实我们可以不要写。我们直接让learning rate去乘以2,再计算一次。可以看到其实结果是一样的。所以呢,我们上面的这个乘以2是可以不要写的,就把它省略掉。
我把这个乘2删掉再执行一次。这次呢,就是6040然后变成5656。这个是我们只有更新一次的结果,只更新了一次w跟b的结果。那我们来试试看更新10次会长什么样子呢?我就用一个for回圈,去把这边重复执行10次,让他跑10次。然后呢,我们顺便记录一下wb跟cost的值。这边cost等于把它记录一下,用一个f string来写。那一开始呢,我先写现在是第几次,也就是iteration,第i次更新,然后呢,我们记录一下现在的cost是多少。cost的话就是这个值,然后顺便把w跟b的值也把它记录一下。我们执行看看这样会长什么样子呢?
第0次的时候cost是这个值然后w跟b。我们来看一下cost是不是有真的在下降。是真的有在下降哦,5656到了3161。这边我让他有个间隔好了,再执行一次。这样舒服了许多。但是呢,他小数点有点多,所以会看起来有点不整齐。这边如果我们想要让他小数点都只显示两位的话,可以这样子做。我们在后面写上:.2f,这样子写呢,就是只会显示小数点后面2位。如果你要3位就是.3f,以此类推。那我把w跟b也只显示2位再执行一次。这样看起来就整齐许多了嘛。cost呢,是有在做下降,然后呢,就是w一路更新的值,b一路更新的值。
接着我们来试试看如果给他20次再执行。可以看到cost也是一路在做下降,到20次的时候只剩下1,705。不过呢,现在他好像又变得不整齐了嘛。原因是因为这边到第10次的时候,他占用了两格。那我们如果想要让他占用的格数都一样的话,这边我们可以在他的后面写上:,然后想要让他占用几格,假设我想要让他占用5格那我就写:5。再执行。这样不管它数字是多少,它就是占用5格。这样整齐许多。
这边除了计算cost还有w跟b的值以外,我顺便把w,还有b方向的斜率也记录一下。那这边呢,他一样会有很多小数点,所以我让他只显示2位就好了。再执行一次。这样就都记录到了。那我们看到cost的地方,可以看到他还是明显的在下降。所以呢,我让他再跑多次一点。跑个100次。往下看,还是好像还在下降嘛。但是我们可以看到右边这边,似乎又开始变得不整齐了。这边又开始变得不整齐了。原因是因为我们的数字还是有大小嘛,有可能是两位数,三位数,所以他还是会变得不整齐。这边如果我们真的要让他变得很整齐的话,可以用科学符号的显示方式。这边我可以写.2e,把它改成.2e。那它就会显示两位。其他呢,以科学符号的方式呈现。什么是科学符号呢?我们执行看看就知道了。可以看到这个就是科学符号,它写5.66e+03,也就是5.66乘以10的3次方的意思,也就是相当于5,660。如果这边你看到的是5.66e-03,那也就是5.66乘以10的-3次方。我们这边用科学符号来表示就整齐了许多。
我们看到cost的这个地方,他还是在下降嘛,所以我再让他跑多次一点。这边是让他跑了100次,那我让他跑个1,000次。再执行。我们来看一下,好像还在下降哦,42然后到41还在下降中。那我们就再让他跑多次一点。我让他跑个一万次。那这边1万次都把它印出来就太多了,所以呢,我让他1000次才印一次就好了。那这边我就判断如果i,他去除以1,000是整除的话,我才印出这些资讯。好,再执行一次。那他就1,000次才会印一次。好,那我们可以看到,这边好像还是有点不整齐哦。原因是因为他多了一个负号嘛。那这个问题要解决的话,我们可以在前面加一个空白。加一个空白的话,它就是会多让出一位来表示符号,表示这个符号。让这边都多让出一位。再执行一次。这个问题就解决了。我们来看一下cost的地方还在持续下降中。跑了1万次之后呢,好像还在下降哦,他从3.51乘以10的一次方到了3.39。那我们再让他跑个跑个2万次来看看。这边我小数点让他显示多位一点好了。这边我让他.4e。好,再执行。那这样他就会显示4位。好,还是持续在下降,但下降幅度变得很小了。我们就让他跑个2万次就好了。可以看到下降幅度慢慢的越来越小。我们看一下旁边斜率的地方,可以看到w方向的斜率跟b方向的斜率也非常非常的小,快要接近0了。
那我就直接把这个gradient descent的过程,把它写成一个函式。这样可以方便之后做使用。函式的名称我就把它叫做gradient descent。那他要传入的东西呢,就有很多喽,x跟y也就是我们的资料,以及初始的w,还有初始的b,然后呢,学习率,以及我们要用来判断好坏的cost function,还有要计算斜率的gradient function。最后是你总共要跑几次run iter,跟你多少次要印出资料我叫他p iter。那p iter我让他预设值是1,000,也就是1,000次会印出来一次。所以这边呢,就把它改成p iter。这个2万呢,就把它改成run iter。然后初始的w是w init,所以我让w等于w init。初始的b呢,是b init。接着计算cost的function呢,要改一下compute cost,把它改成这个function。然后compute gradient也把它改成这个。
那这边我顺便把过程中的cost,还有w跟b的值都把它储存起来。cost的话我把它叫做c hist,然后让它是一个列表。那w的话我把它叫做w hist,b也是b hist。这三个列表就是拿来储存我们跑的这么多次,里面的每一次的cost,还有w跟b的值。这边我就把每一次更新之后的wb还有cost把它储存起来。所以呢,w hist.append,把w储存进来,然后b也是,cost也是,把它储存进来。接着最后我就可以做回传的动作。那回传呢,我就是把最终的w还有b把它做回传,以及我们过程中的所有w,过程中的所有w还有b跟cost全部都回传。
好,我们把它执行。他说有些问题我来看一下。打1,000的这个地方哦,我少了一个:。好,再执行。没有问题。没有问题之后我们就来使用看看。这边再创建一格。那要使用之前呢,我们要设定好初始的w跟b。假设我初始的w跟b呢,我都让它等于0。然后learning rate我就让它是0.001。那这里我也可以用科学符号的写法,我写1.0e然后-3。那就是1.0乘以10的-3次方,也就是0.001的意思。那cost function呢,这边我们就要传入compute cost,就是我们计算cost的那一个函式。gradient function我们写好的是compute gradient。这边我们是可以把函式当做参数传入的哦。然后呢,是run iter。那run iter呢,我让他跑个2万次。最后这个p iter,它预设值是1,000,我就不动它。我可以不动它,我就可以不用设定它。
好,那我们执行看看。它会回传这5个值嘛,所以呢,我也把它写一下。这个w呢,就是最后的w,我叫它w final。b也是最后的b,我叫它b final。然后是储存的wb还有cost。好,我们执行。它怎么只有印出来一次呢?来看一下为什么只有印出来一次。哦,这个return的地方,我不小心把它写到for回圈里面了。所以呢,应该在外面。再执行一次。这样应该是没问题。1000。
好,我们来看一下cost持续在下降中,然后斜率的地方也是在做下降。最终的w,可以看到是9.17多,然后b呢,是差不多在27左右。这边我也把它印出来,来看一下最终的w,最终的w跟b值。好,我们来看一下。最终的wb是9.14多,然后呢,27.88多。这边一样我让它不要那么多小数点,我让它显示两位就好。所以,我写.2f。最终的wb呢,就是9.14以及27.89。
那我们就可以拿这个最终的值去做预测啦。大家应该还没忘记,我们想要解决的问题吧?我帮大家复习一下。假设今天你是一个新创公司的老板,你想要聘雇你的第一个员工,但是你不知道应该给他多少薪水。所以你就到市场上去收集了一些这个职位的相关资料,就是他的年资以及他对应的薪水。那现在呢,你是想要用一条直线来表示这些资料,表示完之后你就可以用这条直线来预测,应该给这位员工多少薪水了嘛。那现在呢,我们已经找到这条直线了。那我们就可以做预测的动作啦。
假设今天来应征的这位员工,他告诉你他有3年半的工作经验,也就是说他的年资有3.5年。那我们就可以帮他做个推算,应该给他多少薪水了。这边呢,他的年资是3.5,我们就可以用找到的这个w final去乘以它的3.5,然后呢,再加上b的值也就是b final。因为我们是用一条直线来表示这些资料嘛,一条直线就可以写成w乘以x加上b。现在我们的x呢,就是3.5。那这个呢,单位是k。执行。他就说年资3.5,大概可以给他59.88多少k。这边一样小数点太多了,我就给他个2位就好了,呃,一位就好了,一位小数点。好,再执行一次。那就要给他59.9k。预测出来大概可以给他59.9k。这里再多个空白。
假设又来了一位员工,他告诉你他的年资呢,是5.9年。那我们就预测一下。这边我再多打两个字预测,预测薪水。好执行。他告诉你他年资5.9的话,那大概预测的薪水也是59.9k。没有,这边我要把它改成5.9。执行。那预测的薪水大概就是81.8k。這樣子呢,我们的问题就解决了。我们可以这样子来预测薪资。
接着呢,我把一些数据也把它画成图来看看。像是这边的cost,我可以把它画成一张图来看看。这边要画图的话,我们就可以用画图工具matplotlib,然后呢,还会用到numpy。我们就把这2万次更新,cost下降的过程把它画出来看看。我们可以用plt.plot把它画成一条线。那x的值呢,这边我们用np.arange,2万次嘛,所以就是0~20000。然后y的值呢,就是我们的cost的历史资料,它的更新的资料都储存在这里啦。我们就可以把它显示出来看看。好执行。可以看到他长这样。那我再帮他加上一些标签跟标题。设定他的title。那title呢,这边是总共是2万次更新,然后跟他的cost嘛,所以我这边写iteration vs cost。然后再帮他加个x label。x轴的话就是更新的次数嘛,就是iteration。然后y轴就是cost。再执行一次。这样子标题还有标签就都上来了。
那我们从这张图可以发现,他前面呢,下降的非常快,但是后面呢,就比较趋缓一点。如果我们想要细部的看前面这一段的话,假设我只想看到100,0~100。那这里我就把它改成0~100。然后呢,这里我也就写:100,也就是前100笔资料。好,再执行。那这就是他前100次更新的下降过程。如果你想要看其他区间的话也是可以,就自己去设定。这边好像少了一个a iteration。好,再执行。
最后我们也可以把w跟b,他更新的过程,这2万次更新的过程把它用图画出来。我们先到之前cost function这边,把3D图复制过来。3D图在这里。我们把画3D图的这个动作把它复制过来。那这边的话,当时我们是设定w跟b都在-100到100之间嘛,所以呢,w跟b从-100到100之间的这个计算也要复制过来。这里的w跟b从-100到100然后它的cost。这里我就再开一格做计算。我先让他计算。那这边我们会用到中文字体,所以呢,还要去做下载字体的动作。一样我到之前的cost function这边,找到下载字体的地方复制过来。再开一格让他做下载。好,这边我们就等他一下,让他执行完计算安装跟绘图。OK,画好了。我把它的角度调一下。它本来是0度0度,我让它是20度跟-65度。好,再执行一次。这个角度不错。红色这点是最低点嘛。接着我们就把w跟b的更新过程,用线的方式把它画上去。那我们要画线的话可以用plot,里面就传入我们储存下来的w hist,b hist以及cost的hist,也就是c hist。这边呢,我先执行看看。可以看到他就把线画出来了。只是呢,这样子的话我不太知道哪里是初始点。所以把初始点也把它画出来。要画点的话就用scatter。这里我直接用复制的。那我们要画初始点的话就是他的第0个值,b hist也是第0个值,cost一样是第0个值。这边我让它颜色是绿色。再执行一次。可以看到绿色这点呢,是我们初始的位置,然后呢,他一路更新更新更新更新到这边,基本上就是到最低点了。OK,一路这样更新过来。
我觉得现在这个颜色有点碍眼,这个曲面的颜色有点碍眼,所以我把它的颜色把它取消掉。这个边框我也觉得没有必要。所以呢,再执行一次。这样子看起来好一点。我把它的这个不透明度再设定的第一点,我让它是0.3。再执行。这样看起来舒服许多。它是一个这样子的曲面,然后呢,我们这个绿色的点是初始点,它一路更新更新过来,基本上就是快要到最低点了,因为红色的点是最低点。
好,那这边我也可以玩玩看,如果我在做gradient descent的时候,设定的是不同的值呢?这边我复制一份到下面,复制到这里。如果这边我的初始点我是设定在-100跟-100的地方,我们试试看。这样子会有什么样子的结果呢?跑完2万次之后呢,一样我们会得到最终的w跟b,还有储存下来的每一次的wb还有cost。我们就再执行一次这边的绘图,看一下会长什么样子呢?可以看到这次呢,他的初始点在这里,然后他就一路往下往下往下走走走....然后,再这样转弯过来,一样是接近快要到最低点的地方。
接着我还可以试试看如果这边,我没有让他更新这么多次,我只让他更新个1,000次。好执行。我只让他更新1000次,我看一下会长什么样子呢?他这次往下走走走,只走到这边就没有继续往前喽,因为我们更新的次数不够多。
你也可以调整其他的,比如说learning rate。我把它调的大一点,我让他是1.0*10的负二次方,也就是0.01。那这里我再试一次。我让他这么大的话执行看看。可以看到他还是有到达最低点。那这边你可能会发现,我们设定的初始点不是在-100 -100嘛,那照你说初始点应该在这里,为什么在这里呢?原因是因为其实呢,我们这边里面储存的是第一次更新的结果。所以呢,他第一次更新之后就跑到这里了。-100,-100是这里初始的地方,他第一次更新就跑到这了。这里面储存的是从第一次更新到最后一次的结果。
那我再试试看,如果把learning rate再调大一点,假设我这里改成5.9。好执行。我们看一下会变成什么样子呢?可以看到他这一次的跨步就非常大喽,他变成这样跨来跨去的,但是好像还是有到最低点嘛。那如果我们再让他更大呢?这边我把它改成1.0*10的-1再执行。好执行。可以看到发生可怕的事情了。我们的那张图那个曲面都不见了。为什么呢?原因是因为他已经超出范围太多了。我们在画那个曲面的时候设定的值是w跟b都在-100跟100之间。那他现在已经超出太多了,所以才会导致曲面都不见了。当我们的learning rate设的太大的时候,他是有可能每一次的更新,不是离最低点越近哦,反而是离最低点越远。我们这个例子就是每一次的更新他都离最低点越远,所以呢,就会导致这样子的状况,整个曲面都不见。
刚刚那个例子就像这样,你如果learning rate设的太大的话,他是有可能越走越远的哦,离最低点越走越远。第一步跨到这,第二步跨到这,第三步他就跨到不知道去哪里了。那这边大家就可以自己去玩玩看,设定不同的wb初始值,还有设定不同的learning rate,以及设定不同的更新次数。
这个就是我们的gradient descent的实作。做完了简单线性回归之后,我小小的帮大家总结一下机器学习的过程。以我们这个例子来说,首先第一步就是你要先准备好资料。那根据这个资料分布的样子,我们觉得可以使用一条直线来表示它。所以呢,我们用一条直线来表示这些资料。接着我们就是要去找出,最能表示这些资料的那一条直线,也就是最适合这些资料的那一条直线。那什么样子的直线是最适合这些资料呢?我们总要给他一个评判的标准吧。所以呢,我们就去设定了,只要这些资料点跟这条直线的距离平方加总越小的话,那我们就说这条直线越适合这些资料。反之如果越大的话那就是越不适合。好,有了评分的方式之后,接着我们总不可能用暴力穷举的方式,去穷举出所有的直线,然后再帮他们全部打个分数,再从中找出最好的那一个吧。样子做太没效率了,我们必须用有效率的方式去找出最适合的直线。那这边呢,我们使用的就是gradient descent,梯度下降法。
那其实这整个过程呢,大概就是机器学习的流程。你套用到其他例子也是一样的。首先呢,你都是要先准备好资料,然后你要根据你的资料去帮他设定一个模型。用这个例子来说,我们设定的模型就是一条直线。设定好模型之后,它里面可能会有一些参数你要去做调整。以这个例子来说就是要调整w跟b。那怎么样调整叫做好,怎么样调整叫做不好,我们总要给他一个评判的标准吧。所以呢,我们要设定一个cost function。以这个例子来说我们设定成这个样子。接着我们不太可能去暴力穷举所有的参数组合,然后都去计算他们的cost,再从中找出最好的那一个。这样子做太没效率了,我们必须用有效率的方式去找出最好的参数。要用有效率的方式,也就是我们要去设定一个optimizer。那这个optimizer呢,翻成中文是优化器。以这个例子来说,使用的优化器就是gradient descent。这个大概就是简单的机器学习流程。你不管他用到其他的例子,其实呢,都是差不多的。一样你都是要先准备好资料,接着呢,设定一个模型,然后,再设定一个cost function,最后呢,要设定一个optimizer。
OK,第二个要跟大家介绍的模型叫做多元线性回归,英文是multiple linear regression。它其实跟我们之前介绍的简单线性回归是差不多的,只是呢,它可以有比较多的特征。好,我们先来看一下,今天想要解决什么问题。在上一个简单线性回归的例子里面,我们是拿年资去预测薪水嘛。但是大家仔细的想一下,我们只单看年资就想要预测薪水,是不是怪怪的?我们应该考虑更多的因素吧。所以呢,现在我们去收集了更齐全的资料。现在除了年资以外,我们还收集了他的学历以及他工作的地方。所以我们现在是想要透过年资、学历以及工作的地方去预测他的薪水。也就是说我们的特征多了学历还有工作的地方。
如果我们一样是想要以一个线性的模型来表示这些资料的话,那就可以使用到多元线性回归。多元线性回归,用数学的式子写出来长这样。它可以写成y等于W1乘以X1,加上W2乘以X2,加上W3乘以X3... 看你有几个特征就可以一路乘下去,最后呢,加上一个b。那以我们这个例子来说有三个特征,分别是年资、学历以及工作的地方。所以数学式子写出来会长这样。我们想要预测的是月薪,月薪呢,就会等于W1去乘以年资,我们的第一个特征,加上W2乘以学历,第二个特征,再加上W3乘以第三个特征工作的地方,最后加上一个b。那我们的目标呢,就是要找出一组W1、W2跟W3以及b的组合,让他最能表示这些资料。这也就是我们多元线性回归要做的事情。
在开始去找最适合的w跟b之前,我想大家应该有发现一个问题,就是以我们这个式子来说,我们拿W2去乘以学历,然后又拿W3去乘以工作的地方。但是呢,我们可以看到,学历跟工作的地方这两个特征他们都是文字。要怎么样让文字去成一个数字呢?这样不太行嘛。所以呢,我们必须先把这两个特征做一些处理,先把它从文字转换成数字,才有办法做这里的计算嘛。
我们先来处理学历这个特征。学历这个特征总共有三种可能的值,分别是高中以下、大学跟硕士以上。从这个特征我们可以看得出来,它其实是有高低关系的。如果有高低关系的话,那我们是不是可以用数字的0、1跟2来表示这三种状况?0就拿来表示最小的高中以下,1呢,就表示大学,2就是硕士以上。如果一个特征它是存在大小关系,它是存在高低关系的话,我们就可以用这样子的方式来把这些文字取代掉。这样子的取代方式呢,它有一个名称的,它叫做label encoding。我们把刚刚的学历取代掉之后就会变成这样子。回来来看一下,硕士以上我们是用2来表示,大学是用1,然后高中的话是高中以下的话是0。所以这边就会变成1,2,0,下面呢,以此类推。
那我们就先来实作一下这个label encoding。首先一样先把资料读取进来。读取的动作跟之前都是一样的,所以我直接用复制的。不过呢,在网址的部分,因为我们这一次的资料它是多了两个特征,所以呢,有些改变。我们在salary data这边,因为它是第2版,所以我们在后面写上2。一样读取之后,我们把它显示出来看看。OK,我们的资料长这样。总共有三个特征分别是年资、学历、学历以及工作的地方。我们现在是想要先处理学历这个特征,想要先把它从文字转换成数字。好,那这边我们可以这样子做转换。我们去取得资料的,看一下学历这个特征,它叫做education level。我们去取得这个特征。好,先把它显示出来看看。他本来长这样嘛。我们想要让他的大学变成1,然后硕士以上是2,高中以下是0。这边我们可以这样子做。在后面写上.map,然后呢,在里面写上字典。我们想要对应的是高中以下让它对应到0,接着呢,大学让它对应到1,最后是硕士以上让它对应到2。我们这样子写呢,它就会帮我们做转换。把这一个特征,它里面的值呢,做这样子的转换。转换完之后,我再把这个特征的值把它做改动。这样子改动过来之后呢,我们再把整个data,把它显示出来看看。可以看到他现在就会变成对应的值啦。这边本来第一笔资料是大学嘛,然后第二笔是硕士以上,第三笔是高中以下,所以它就会变成1,2,0,然后下面也是做同样的转换。那这样子我们的label encoding就完成了。
处理完学历这个特征之后,接着我们来处理工作的地方这个特征。这个特征总共有三种可能的值,分别是城市a、城市b跟城市c。那这边你可能会想,我们是不是可以用同样的方法,我们用label encoding把城市a、b、c用0、1、2来表示。但是你仔细想一下,你觉得这样子可以嘛?我们并不知道城市abc之间,它有没有存在一个高低的关系,或者存在一个大小的关系。如果我们用0、1、2来表示的话,那谁要是0,谁要是1,谁要是2呢?这种不存在大小关系的特征,如果我们想要把它做转换的话,可以 त्याची它转换成多个特征。像是这个样子。我们本来是只有一个城市的特征,那我把它改成城市a、城市b跟城市c,变成三个特征。这边的第一笔资料,它本来是在城市a工作嘛,所以呢,我们就给他城市a这个属性是1,然后城市b跟城市c这两个属性是0。好,再看第二笔资料。第二笔资料呢,它原本是在城市c工作,所以我们就给它城市c这个属性是1,然后其他给它0,以此类推。这样子的做法呢,叫做one hot encoding。只要是我们想要把一个不存在大小关系,不存在高低关系的一个特征,把它从文字转换成数字的话,那我们就可以使用one hot encoding。
它就会从一个特征变成多个特征。看这个特征它原本有几个值,有几个可能的值,那它就会变成几个特征。以我们这个例子来说,它可能的值就是城市a、b跟c,那就会变成3个特征:城市a、城市b跟城市c。
那这样子把它转换完之后,其实我们还可以把这3个特征里面的其中一个把它删掉。为什么呢?原因是因为这三个特征里面,其实我们只要知道其中两个特征的值,就可以推导出第三个特征的值了。
假设我们现在把城市c这个特征删掉,我们来看一下,有没有办法通过城市A、B来推导出城市c呢?现在长这个样子,如果城市a是1,然后b是0的话,那城市c肯定就是0嘛,因为呢这三个特征里面只有其中一个会是1。好,再看第二笔资料,城市a是0、城市b是0,那城市c就一定是1嘛,因为它不是城市a,不是城市b,肯定就是城市c。以此类推。
所以呢,我们是有办法透过其中两个特征,就推导出第三个特征的值。那这个时候呢,我们就可以把其中一个特征把它删掉。
好,我们再举一个比较简单一点的例子。假设你现在有一个特征叫做性别,它的值呢只有两种可能,要么是男生,要么是女生。那显然这个特征它也不存在高低关系,不存在大小关系嘛,所以我们可以使用one hot encoding,把它变成两个特征分成男生跟女生。但是呢,他不是男生就是女生嘛,所以我们可以从其中一个特征,就推导出另外一个特征的值。这样子我们就没有必要把它分成两个特征嘛,因为过多的特征反而会让我们的运算变得更加的复杂。所以呢,我们就可以把其中一个特征,把它删除掉。
好,所以回到原本的例子里面,城市a、b、c这三个特征里面,我们只要知道其中两个的值就可以推导出第三个。那这样子我们就可以把其中一个把它删掉。这边我们是选择把城市c这个特征删掉。
这边提醒一下大家,并不是所有可以被推导出来的特征我们都要把它删掉,因为有些特征即便可以被推导出来,但是它存在着特别的意义,或是它可以加快我们运算的效率,那我们就不会把它删掉。但是呢,在one hot encoding的这个例子里面,我们经过转换后是可以把其中一个特征删掉的。
好,那接着呢,我们就直接来实作one hot encoding。现在我们的资料长这样,我们是想要把city这个特征把它做转换,想要把它从文字转换成数字。那以这个特征来说,我们可以使用one hot encoding把它做转换。这边我们可以使用sklearn,它底下的preprocessing,然后呢我们去使用它里面的one hot encoder。sklearn这个套件呢,它提供了非常多我们在做机器学习的时候会使用到的东西,像是我们现在引入的这个one hot encoder,它就可以帮我们快速的做转换。
这个one hot encoder呢,它是一个类别,所以我们先把它做一个创建,我们先创建一个转换器,或者你可以说先创建一个编码器。这边呢,我就把它叫做one hot encoder。创建好之后,我们可以先让这个编码器,让这个转换器去看过我们的这个特征,城市这个特征。所以呢,这边我们可以写点fit,然后去让他看过city这个特征。不过这边要特别注意的是,他只接受传入的是一个二维的矩阵,所以呢我们这样子写是不行的,因为这样子写呢是一个一维的矩阵。想要让他变成二维的,我们就再加一对中括号,让他用两对中括号,这样子就会是一个二维的矩阵。让他看完这个特征的所有值之后,接着我们就可以把它做转换了。我们可以使用它底下的transform,来做转换。里面一样传入我们要转换的那个特征,那这边一样是要一个二维的矩阵,所以要两对中括号。这里我就把它转换的结果叫做city encoded。接着我们直接把它显示出来看看。
执行可以看到转换的结果长这样。为什么会长这样呢?原因是是因为这个one hot encoder经过转换之后,预设它会回传给我们的东西是一个稀疏矩阵,OK,是一个稀疏矩阵。如果你不知道什么是稀疏矩阵那也没关系,我们这边想要看到的是,他回传给我们一个完整的矩阵。那完整的矩阵我们可以在后面再写上toarray,那这样子呢,他就会回传完整的矩阵给我们。
再执行。可以看到这就是我们要的结果啦。它会把一个特征里面总共有3种可能城市a、b、c,把它变成3个特征,OK,变成3个值。那这边我们就可以把转换的结果,把它替代掉原本的这个特征,替代掉原本的这个city这个特征。我们可以这样子做。我们原本的资料是data嘛,我们执行一次。OK,原本的资料长这样。如果我们想要帮他再多加3个特征的话,也就是再多加3列的话,OK,是3列,我这边再强调一次,在本堂课里面是横行直列,横行直列。如果我们想要再帮他多加3列,多加3个特征的话,我们可以这样子写。写两对中括号,我想要帮他加一个cityA,然后呢再加一个cityB,最后再加一个cityC。然后我们可以把它的值呢,就指定成我们刚刚转换之后的这一个结果。接着我们再把它显示出来看看。
可以看到这样子写呢,它就会多加3列,多加3个特征cityA BC。接着我们就可以把city这个特征删掉啦,因为它已经被转换成这3个特征了。然后呢,这三个特征里面,我们又可以删掉其中一个。我把cityC把它删掉。那我们可以这样子做。我们可以写data.drop,然后在第一个参数写上我们想要删掉的特征,想要删掉的特征用一个列表来表示,想要删掉city,还有cityC这两个特征。接着呢,第二个参数写上,我们想要删掉的这个东西它的轴线是在哪里axis。我们想要删掉的东西它是列嘛,city跟cityC这两列。想要删掉的东西它是列的话,那axis轴线我们就要指定它是1。如果我们想要删掉的东西是行的话,那这边我们就要指定轴线是0。好,所以这边我们写1。接着呢,我们就把删掉的结果把它显示出来看看。
执行。可以看到现在呢,city跟cityC这两个特征这两列就都不见了。那这样子我们就把工作地点这个特征也处理好了。
把文字都做完转换之后,接着通常我们在训练模型的时候,还会把资料分成训练集跟测试集。训练模型就是找出最适合的参数,以我们这个例子来说,就是找出最适合的w跟b。通常我们在训练模型的时候,不会把所有的资料都使用上,我们只会使用到一部分。那另外一部分要做什么呢?另外一部分是要拿来做测试用的。因为你想哦,如果我们把所有的资料都拿去做训练,然后呢找出了一组最佳的w跟b,但是我们要怎么样验证这一组w跟b,它的效果是如何呢?我们是不是要测试一下?那要测试的话,我们就要把它测试在陌生的资料上,我们是不会拿训练的资料来做测试的,因为训练的资料机器都已经看过了,就相当于是他已经知道答案了。那在他已经知道答案的情况下,我们还拿这个资料来做测试这样就不会准了嘛。
所以呢,为了有陌生的资料可以拿来做测试,我们通常会把资料分成训练集跟测试集。像是这个样子,假设我们有10笔资料好了,那通常呢训练集会占7成到8成左右。以这个例子我们是占8成,所以有8笔资料会是训练集,然后呢有2成的资料,也就是有2笔资料会是测试集。训练集呢,就是拿来找最佳的w跟b,那测试集呢,就是我们找出最佳的w跟b之后,拿来做测试看他的效果怎么样。
了解了什么是训练集跟测试集之后,我们就直接来实作啦。OK,现在我们的资料长这样。首先呢,我们先把x跟y把它分出来。大家应该还记得吧,我们想要使用的模型是多元线性回归。多元线性回归写成数学的式子就是y=W1*X1+W2*X2一路乘下去看你有几个特征,最后呢加上一个b。那以我们现在这个例子有4个特征,分别是年资、学历还有工作的地方。那我们要预测的y呢就会是薪资。所以我们先把x跟y把它分出来。
这边x就等于我们去取得它的四个特征,第一个是年资,第二是学历,然后是cityA跟cityB。然后呢y就会等于我们去取得它的薪水。这里我把x跟y把它显示出来看看。x是这4个特征,然后y呢,就会是薪水,没有问题。
接着我们就可以把它分成测试集跟训练集啦。这边我们一样可以使用sklearn这个非常好用的工具,使用它底下的model selection,然后我们去引入他底下的train test split。接着就可以使用它来帮我们分成测试集跟训练集啦。在第一个参数里面传入我们的x,也就是特征,然后第二个参数里面传入y。接着我们可以去指定测试集的大小要多少,这边我们可以写test size,那我想要他是占2成就好,这边我可以让他等于0.2。这样子写呢,他就会自动把我们资料的2成分出来当做测试集,然后另外的8成分出来当做训练集。如果你想要测试集占3成的话,这边你就可以写0.3,以此类推。
那这样子写呢,他会回传4个值给我们,分别是拿来训练的x跟拿来测试的x,还有拿来训练的y跟测试的y。所以第一个呢,我就叫他x train,然后第二个我叫他x test,接着就是y train以及y test。他会回传这4个值给我们。那我就把它显示出来看看。我显示出来x train,就是我们拿来训练的x。可以看到它就取出了这几笔资料当做训练集。那我们来看一下它的长度是多少。来看一下它的长度是多少,它的长度是28。那我们看一下原本的x,它的长度是多少,原本的长度是36。我们拿2成当做测试集,也就是8成当做训练集。那我们拿36去乘以0.8看一下是多少,是28.8。所以呢,他28.8,他 স্বয়ংক্রিয়ভাবে把这个点8消掉了,就拿28笔资料当做训练集。那我们顺便看一下测试集他的长度是多少。可以看到总共呢x它是有36笔资料的,然后我们经过分割之后,它有28笔拿来当做训练集,然后8笔拿来当做测试集。这边y也是一样的。
我们再把x train把它显示出来看一次。这边你会发现一件事情哦,就是我们每次执行的时候,这个x train好像它都会变动哦。我们看一下第一笔资料是5.1,0,然后1,0嘛。我们再执行一次,又变了,变成6.9,2,1,0。再执行一次,又变了,7.8,2,0,0。为什么会变呢?原因是因为这个分割的过程,它预设会帮我们随机分割,所以呢你每次分割的结果都会不一样。如果你想要让他分割的结果是固定的话,这边我们还可以再设定一个参数,叫做random state,然后你可以给他指定一个数字。每一个数字他都会对应到不同的分割情况。假设我给他的数字是87。好,我们再执行一次。可以看到这一次它分割的结果呢,长这样,第一笔是4.6,1,1,0。现在在我固定random state等于87的情况下,我再执行,可以看到它是不会变的。但是呢,如果我把这个数字把它做改动,我把它改成86,再执行,他就又会变了。那如果我在不改动86的情况下,再继续执行,他就不会变。所以这边如果你想要固定分割的结果,你是可以给他指定一个数字的。这边我就给他指定87,因为我想要它分割的结果是固定的,这样方便我后续做演示。
好,那我把x test也把它显示出来看看。它是长这样,总共有8笔资料。那y train呢,也把它显示出来。OK,有这些。然后y test。好,那这样子呢,我们就成功的把测试集跟训练集把它分好啦。
最后,为了后续方便的计算,我会先把x train跟x test把它转换成numpy的格式。现在他们都是pandas的格式,所以执行之后会很漂亮。OK,会有这样的格子。那如果我把它转换成numpy的格式的话,它会长这样。我可以在他后面写to numpy,那它就会变成一个矩阵的样子。变成这样子矩阵的样子就会比较丑,不过它会让我后续的计算比较方便。所以这边我先把它做转换。x test也是一样,我也把它做转换,可以让我后续的计算比较方便。
把文字都转换成数字,然后也分成训练集跟测试集之后,我们就可以回到模型的地方啦。我们想要用的模型是多元线性回归。那它可以写成这个样子,只不过呢,我们的特征已经从3个变成了4个。这个工作的地方呢,已经变成了cityA跟cityB这两个特征。所以我们要改写一下变成这样。
那我们现在的目标呢,就是要找出一组W1 2 3 4跟b的组合,让这里预测的月薪可以越接近真实的资料。好,那我们就先来实作一下这个部分。首先呢,我先来设定w跟b的值。一开始我都先随便设定。w它有1 2 3 4嘛,所以我用一个一维的矩阵来表示它。这个一维的矩阵里面有4个值,所以这里我先引入numpy,把它叫做np。接着呢,我就创建w,然后让他是一个矩阵里面有4个值,假设我就让他是1 2 3 4。这个1 2 3 4呢,就是表示我们的W1 2 3 4。接着还要设定一个b的值,一样我随便设定,我让它是0。
设定好w跟b之后,我们是想要让W1去乘以第一个特征,W2乘以第二个,W3乘以第三个,W4乘以第四个嘛。那现在因为我们是在训练阶段,所以我们要乘的特征是在x train这里。我先把x train把它显示出来。那它长的是这个样子,它总共有4列,每一列都代表一个特征。所以我们想要的是,这个1去乘以第一列,然后2去乘以第二列,3乘以第三列,4乘以第四列。我们想要这样子乘的话,可以直接这样子写。我们直接让x train乘以w,那它就会自动让1去乘以第一列,2乘以第二列,以此类推。
好,我们执行。这是乘下来的结果。这边的第一列就是W1乘以X1,第二列就是W2乘以X2,以此类推。那我们想要的是他们做相加嘛,W1乘以X1加上W2乘以X2,然后加上3加上4。也就是我们想要把这里的每一行做相加。我们想要把它每一行做相加的话可以这样子做,先把这边计算的结果把它括号起来,然后再使用点sum。如果这边我只有写这样子的话,他会把这里的每一个值都做加总。不过我们想要的是每一行做加总嘛,我们想要的是横的做加总。那这边我可以再多设定axis等于1。等于1就是横的做加总。如果你想要直的住加总的话,你这也可以设定等于0。好,那我们来试试看执行。
这就是加总的结果。这里的每一个值呢,就是刚刚的每一行它的加总,也就是W1乘以X1加上W2乘以X2加上3加上4的结果。这里都加完了之后,最后我们还要加上一个b嘛,所以呢,这边我加上一个b。这边加上b的话,他就会让里面的每一个值都加上b。不过我们现在b设定成0,好像看不太出来,所以我把它设定成1好了。我们再执行一次。那这样加呢,这里的每一个值都会加1。所以现在这里计算出来的每一个值,就是我们预测的薪水啦。我把它叫做y pred。
那接着呢,我们就是要找出一组最适合的w跟b,要找出一组最适合的w跟b的组合。那我们首先就要先定义什么叫做最适合,也就是我们要去设定一个评判的标准,那就是我们要去设定一个cost function。这里的cost function,我们可以设定的跟之前简单线性回归的时候一样,因为我们一样是希望预测的月薪,跟真实的资料距离越小越好。所以我就一样把它设定成真实的数据,去减掉预测值然后做平方。这边做平方的原因是因为这边相减有可能会有负数,我们为了计算方便,就直接把它做平方,这样就不会有负数了。所以我们现在的目标呢,就是让真实数据去减掉预测值然后做平方,它的值越小越好,也就是这边的cost越小越好。
好,那我们就直接来实作cost function。我们的cost function是设定成,真实数据去减掉预测值然后做平方嘛。预测值的部分我们上一步已经做好了,这边的y pred就是预测值。我先把它显示出来看看。这边是我们的预测值。真实数据的部分呢,因为我们现在是在训练阶段,所以真实数据就是y train。所以我们是拿y train去减掉y pred。我一样先把y train把它显示出来看看。OK,它总共有这几笔。我们拿y train去减掉y pred,它就是把每一笔做相减。相减完之后我们还会把它做平方。好,执行。
可以看到这边的每一个值呢,就是我们拿真实的薪水去减掉预测的薪水,然后做平方的结果。那我们是希望这里的值越小越好嘛,想要这里的值越小越好,我们可以计算它的加总或是计算它的平均都可以。这里我就计算它的平均,希望它的平均呢越小越好。计算它的平均可以把它括号起来在后面写上点mean。好,执行。
可以看到这是平均的结果。那这里我就直接把计算cost的过程把它写成一个函式,这样可以方便我们带入不同的w跟b。我把它叫做compute cost,它一样是传入x跟y,也就是我们的真实数据,然后呢再传入w跟b的值。里面就是先计算y pred的值,也就是我们的预测值,只不过这边的x train把它改成x,计算出预测值之后,我们就可以计算cost了。计算cost呢,就是这里。这边的话一样y train我把它改成y,真实数据去减掉预测值做平方然后再取平均。这个呢,就是cost。好,那最后就是return cost。
直接来试试看。这边我们就直接来使用它compute cost。这里要传入的呢,就是x train跟y train,因为我们现在是在训练阶段嘛,然后w跟b呢,我一样传入这两个值。一样先传入这两个值,我们执行看看。可以看到计算的结果跟我们上面是一样的。那如果我把这边的b设定成0呢,然后这边设定成0 2 2 4。好,再执行一次。这样子的w、b组合计算出来的cost就比刚刚的还大了,刚刚的是1,772嘛。那这样子呢,我们就把评判的标准,也就是cost function把它定义好啦。
设定完评判的标准,也就是cost function之后,接着我们就是要用有效率的方式,去找出一组w跟b,它可以让Cost越低越好。那这个有效率的方式呢,就是我们要去设定一个optimizer,也就是优化器。在我们这个例子里面,我们一样可以使用gradient descent,梯度下降法。大家应该还记得吧,它就是根据斜率去改变参数。只不过呢,我们之前在简单线性回归的时候,参数只有两个w跟b嘛,那我们现在呢,参数是变成了5个W1234跟b。我们先看一下之前是怎么样更新参数的。
如果我们要更新w的话,就是拿w去减掉w方向的斜率*学习率。要更新b的话,就是b减掉b方向的斜率*学习率。那其实更新参数的方式都是一样的,只是呢,我们从两个参数变成了五个参数。现在呢,我们有W1234跟b这五个参数。那要更新这五个参数呢,就是个别去减掉他们方向的斜率*学习率。
这边各个方向的斜率要怎么样计算呢?其实也是跟之前一样的,我们只要把Cost function去做微分,就可以得到各个方向的斜率了。我们的Cost function是拿真实数据去减掉预测值然后做平方,写成数学式子就是y减掉y pred然后做平方。其中这个y pred的呢,我们又可以把它摊开来,变成W1*X1+W2*X2...乘到4,然后最后再加上b。
如果我们想要知道W1方向的斜率,就是把它对W1做微分,或者准确一点说把它对W1做偏微分。如果你想要知道的是W2方向的斜率,那你就是把它对W2做偏微分。以此类推W3 W4跟b都是一样的。
这里如果你完全不知道什么是微分的话那也没关系,因为有很多工具都可以帮我们自动的做微分。所以W1方向的斜率经过计算之后长这样。可以看到非常长一串,但是呢,其实我们可以发现这边这一串W1*X1+W2*X2加上3加上4,然后加上b,这一段呢,其实就是y pred嘛。所以我把它简化一下就会变成这个样子,它就是2*X1然后去乘以y pred-y的结果。接着用同样的方式去计算出W2方向的斜率,它会长这样。我们可以发现其实呢,他只是把X1替换成X2而已,其他都是一样的。由此可知W3方向的斜率就是把这边替换成X3,那W4方向的斜率呢,就是替换成X4。最后b方向的斜率计算出来会长这样。这边呢,就没有再多乘以一个x了。
在这边我们可以看到,不管是w方向还是b方向的斜率,在前面的地方呢,都有一个乘以2。不知道大家还记不记得,其实我们有说过这个乘以2是可以省略掉的,因为我们在更新参数的时候,后面都还会再乘以一个学习率嘛,所以这个乘以2呢,其实交给学习率来乘就可以了。那这边我们就可以把它全部省略掉,变成这个样子。
好,知道怎么样计算斜率之后,我们回到原本的地方。现在各个方向的斜率我们都会算了。不过呢,他后面还要再乘以一个学习率。这个学习率要怎么设定呢?跟之前一样就是透过测试跟实验。你不能把它设的太大,因为它有可能会直接跨过最低点,又或者是离最低点越来越远。那你也.不能把它设的太小,因为它有可能会走到天荒地老都走不到最低点。设定好学习率也会计算斜率之后,我们就只要不断的去更新参数,让它一步步的逼近最低点的位置,就可以找到我们要的最适合的w跟b的组合啦。
接着我们就直接来实作gradient descent。首先我们先来计算各个方向的斜率。因为b方向的斜率比较简单,所以我先计算它。我把它叫做b gradient,它是y pred去减掉y。那现在是训练阶段,所以我们的y就是y train。这个y pred之前都已经计算过了,所以我直接把它复制下来。不过呢,我们现在是训练阶段,所以这边的x呢,我把它改成x train。我把计算的结果显示出来看看。因为我们的训练集有28笔资料,所以呢这边会有28个值。那我们就把它做个平均,把它括号起来然后使用点mean。这样就可以计算平均了。平均呢,是-46.94多。
计算完b方向的斜率之后,接着我们来计算w1方向的斜率。我把它叫做w1 gradient,它呢,就是y pred去减掉y train,然后前面要再乘以一个x1嘛。那这个x1呢,其实就是我们的第一个特征。我们现在是在训练阶段嘛,所以我们的特征是在x train这里。我先把x train把它显示出来。可以看到它总共显示了四列,那这四列呢,就是我们的四个特征。这里的x1呢,就是第一个特征,也就是这里的第一列。那我们要取得这里的第一列的话,可以这样子写。我们写一对中括号,然后:, 0。这样子写的意思呢,就是第一个维度里面的所有值我们都要,:就是全部都要的意思,然后逗号0,就表示第二个维度里面的值呢,我们只要最开始的那一个。我们可以看到这个矩阵呢,是一个二维的矩阵。那第一个维度里面的所有值我们都要,也就是这边第一个中括号里面的所有值我们都要。这边的所有值我们都要,然后呢,第二个维度我们只要它最开始的那一个值。第二个维度呢,就是这边的每一行,我们只要它最开始的那一个值。那就会是这边的第一列。好,那我们执行。
可以看到它就会把第一列的所有值全部都取出来了,也就是我们的x1。所以这里我就可以把它替换掉,把它替换过来。然后我们把计算的结果显示出来看看。可以看到它总共会计算出28个值,因为我们现在训练集有28笔资料嘛。一样我们就取它的平均,把它括号起来然后取平均。执行。
这样W1方向的斜率就计算出来啦,295 -295.8。接着W2W3W4的计算方法都是一样的,所以这里我直接用复制的。这里呢,就是改成2,改成3,改成4。然后我们要取得的是第二列嘛,那这里就是1,然后2,3。那我们把执行的结果都把它显示出来看看。W1,W2。OK,执行。
可以看到这个呢,就是W1 W234它们方向的斜率。这里我用一个回圈来写会比较方便,所以我重新创建一个w gradient,它里面呢,就是存放W1234方向的斜率。这里我就先创建一个里面都是0的矩阵,我让它有4个值。好,先把它显示出来看看。里面有4个值我都先让它是0。不过这里直接写4好像不太好,这个4的意思是我们有几个特征,因为我们有几个特征就会有几个w嘛。我们想要知道有几个特征的话,是不是可以直接去取得x train,它总共有几列?因为她有几列就表示我们有几个特征。我们想要知道x train有几列的话,这里可以这样子做,我们去取得它的shape,它的形状,它会告诉你它总共有28行4列,也就是第一个维度它有28个值,然后第二个维度呢有4个值。那第二个维度的这个值呢,就是我们要的有几个特征。我们想要取得第二个维度的值,这边呢,我就中括号1,就可以得到4。所以呢,这边我把它改成这样会好一点。接着我就用一个回圈让他去计算这边的斜率,他总共呢,会跑四次嘛,所以一样我就把这里直接贴过来,然后w gradient它的第i个斜率呢,就会是这边计算的方法,不过这边我就把它改成i。这样子应该是没问题。我把计算完之后的结果呢,也把它显示出来。可以看到四个方向的斜率,W1234呢,就是这四个值。
好,那这样子呢,我们就把w跟b方向的斜率都计算出来啦。这个是w方向,这个是b方向。接着一样我把计算斜率的过程把它写成一个函式,这样可以方便之后做使用。我把它叫做compute gradient,那它就是要传入x跟y,也就是我们的真实资料,然后还有w跟b的值。这边我们首先先把y pred把它计算出来。那y pred这边已经计算过了,我直接把它贴过来。y pred的计算完之后呢,我创建一个空白的矩阵,创建一个里面都是0的矩阵,然后呢,这边要改成x,然后呢,用一个for回圈去计算每一个w它的斜率。这里也改成x,这里都改成x。那这边计算完之后,我们还要计算b方向的斜率嘛。b方向的斜率呢,我直接也是贴过来。这里一样改成y。好,那计算完我们把它的结果做回传,w gradient以及b。OK,执行。
那我们就直接来使用看看。这边呢,要传入的x,因为现在是训练阶段,所以x跟y都是x train y train。w跟b呢,我一样传入这边的这个值。好,我们执行看看。可以看到结果呢,跟上面是一模一样的。w跟b都是一样的。我们做个改动,假设我把b变成1,然后这边变成1 2 2 4。再执行一次。那这样子计算出来的斜率就会不一样喽。
好,会计算斜率之后,接着我们就试着来更新参数啦。要更新w的话,就是让w去减掉w方向的斜率,然后呢,乘以一个学习率。更新b的话,就是一样,只是把这边改成b。那这边学习率的话,我先随便设定,假设我让他是0.001。w跟b的初始值呢,我一样让它是这样子。w跟b方向的斜率,我们就直接呼叫这个函式来做计算,它就会把斜率回传给我们了。w跟b方向的。这里我就把w跟b更新的结果把它显示出来看看,看它有什么不一样。可以看到本来是1 2 2 4嘛,然后变成了1.2多,2.0多,2.0多,4.0多。然后b也有改动。
接着我们来看一下它的cost,经过这样子的更新之后,是不是真的有变小了?也就是它是不是真的有往低处去走呢?这边我就直接使用这里的函式来计算cost。在还没有更新之前先计算一次,然后我把它的结果把它印出来。在更新完之后呢,我再把它显示出来一次,看一下会不会真的有变小。可以看到真的变小了,本来是1,800多,然后变成1,675。这边确定他有变小之后,接着我们就是要不断重复这边的动作,让他一直去不断的更新w跟b的值。那这边呢,我们就把它写成一个函式,叫做gradient descent。其实这个函式我们在之前简单线性回归的时候就已经做过了。这个写法呢,基本上是一模一样的,所以我直接把它复制过来。
好,那我们直接执行。执行完之后呢,我们就可以直接呼叫它啦。那这边我们之前也有呼叫过了,所以我直接把它复制过来。不过这边有几个地方要改。这边w跟b的初始值呢,我把它改成这样子。learning rate呢,我是设定成0.001嘛,所以这边也可以写成这个样子。我先让他跑个1万次就好。好,那这边呢,因为我们现在已经分成测试集跟训练集了,所以这边我们现在是训练阶段,我写x train跟y train。后面的地方基本都是一样的,我们不需要改动它。这边compute gradient跟compute cost,我们的名字都是命名的一样。
好,那我就执行它。可以看到它发现的错误。会发生错误的原因是因为这边的w跟w gradient,我们使用到的是numpy矩阵格式。在这里,在这边w跟w gradient,我们使用到的是numpy的矩阵格式。那我们使用numpy的矩阵格式的话,他就没有办法直接写: .2e来把这里的格式做转换。所以这里我先把它都删掉看看。我们再执行一次。这样应该就不会有问题了。可以看到果然就没有问题了。可以看到cost是有在做下降的,然后呢,w的值跟b的值呢,在这边都有在做更新。不过我们没有把它做这样子的格式化之后,看起来就会丑丑的嘛,就会不整齐。
这边是w方向斜率跟b方向斜率。因为w有W1234,所以这边有4个值。这边也是有W1234,所以有4个值。不过现在执行起来很丑嘛。如果我们想要让它漂亮一点的话,可以直接去设定numpy矩阵它印出来的格式是长什么样子。这边我们可以这样设定np.set_printoptions,设定它的formatter,它要是一个字典,然后我希望浮点数它的格式呢,长得是这个样子,冒号空白.2e的这个格式。最后在后面写上点format。那这样设定呢,就相当于我们这样写,就相当于这边这样写,他就会把这个矩阵里面的每一个值,都用这个格式把它印出来。好,那我们再重新执行一次。这里再执行。
可以看到这边就变得漂亮多了。那我们看一下这个cost的地方,是有一直在下降没有问题。这边跑完1万次之后,我们可以发现他好像还在下降,cost好像还在下降。这边我测试看看如果我让他的学习率大一点。好,看来还是有在下降哦,而且下降的明显比刚刚快,所以这个学习率应该是没有问题的。慢慢的好像下降的速度越来越慢了。这边我就先设定这样,然后让他跑个1万次就好。那这边大家可以自己去玩玩看,设定不同的初始w,设定不同的初始b,不同的learning rate,还有跑不同的次数,会有什么样子的结果。
这里我就把它最后找出来的w跟b,把它显示出来看看。可以看到它最终找到的w跟b呢,是这5个值。这里我想要验证这5个值到底是好还是不好,我们就可以把它使用在测试集上面啦。我们拿最终找到的w去乘以x test,也就是测试集。这样子乘呢,就是W1*X1,W2*X2,然后3跟4。这边乘完之后,我们要把它做相加嘛,所以我把它括号起来把它做相加。我想要它做相加的方向是axis等于1。最后呢,还要再加上一个b,加上我们最后找到的这个b。那这边计算出来的结果就是我们在测试集上面的预测,我把它叫做y pred。
我们就可以对比一下,在测试集上面的预测跟真实的值究竟差了多少。这边我把它显示成Pandas的dataframe格式会比较好看一点,它会有格子。我让他有两列,第一列是我们在测试集上的预测结果y pred,然后呢,第二列就是测试集上的真实资料y test。我们来看一下究竟有差多少。执行。这边出现错误,多打了一个u。再执行。还是有错,data。好,再执行。
可以看到左边的这一列呢,是我们在测试集上面预测的结果,然后右边这一列是测试集上的真实资料。我们预测是4.0多,然后真实是43.8,好像没有差很多嘛。67.7,72.7,好像差的还可以。61.6,60。OK,差别感觉还可以。好,那我们就成功的把gradient descent把它实作出来了,也找到了最终的w跟b。我们把最终的这组w跟b,把它使用在测试集上面,结果是这个样子。最后如果我们想要精准一点知道,这样子的预测到底是好还是不好,除了直接去看他们的误差以外,我们也可以计算他的cost,因为cost的值就是我们评判好坏的标准嘛。所以呢,这边我们可以计算他的cost。那我们直接使用compute cost。现在我们是测试集,所以x呢是x test,y是y test,然后w跟b就是我们最终找到的w final跟b final。我们计算看看。它的cost是18.1多。那我们去看一下,对比一下之前在训练的时候cost是多少。我们在做训练的时候,最终cost降到2.5*10的一次方,就是25.2多。这样看起来是没有问题的,因为我们在测试集上面的cost反而比训练集上面还小,那就表示说,他的表现好像比我们在训练集上面还好。所以经过cost的计算以及上面的交叉比对之后,如果这个误差是你可以接受的,我们就可以把这个模型使用在真实的情况上啦。
假设今天真的有人来面试了,这个人他告诉你他的年资是5.3年,然后学历是硕士以上,工作的地方在城市A。经过面试之后我觉得我要录取这个人,但是呢,我不知道应该要给他多少薪水。这个时候我们就可以使用训练好的这个模型,来预测一下大概可以给他多少薪水。
首先我们要先把资料做一些处理,因为这边有文字嘛,我们要先把它转换成数字。在测试集上面怎么样把资料做转换的,在真实的情况就怎么样做转换。这边我就直接转换了。年资5.3,没有问题,直接使用它。那硕士以上我们是用label encoding把它转换成2。然后工作的地方,我们是用one hot encoding,把它变成3个特征,接着又会删掉其中一个,所以会变成2个特征。所以这边城市A,第三个特征就是1,然后第四个特征就是0。这边我用一个矩阵来表示它,我就叫它x real,它是等于np.array。如果这边你是有做特征缩放的话,一样在真实的情况下我们也要做特征缩放。那特征缩放呢,我来往上看,我们特征缩放做的地方在这里。我们的测试集是怎么样做缩放的,那我们在真实的情况就怎么样做缩放。OK,我直接复制过来。我们的x test是这样做缩放的嘛,所以呢,x real也比照办理。
那我们把缩放的结果把它显示出来看看。他说有出一点问题,这边他说只接受传入的是二维的阵列,不接受传入一维的。没有错,我忘记了transform,他只接受传入的是二维的矩阵,不接受传入一维的。所以这边我再加一对中括号,他就会变成二维的了。好,再执行。
可以看到这就是经过缩放之后的结果。那接着我们就可以把它套用到模型里面了。我们的模型是这样子做计算的嘛,我直接复制过来。那这边我们要套用进来的呢,是x real,所以这边把它改掉。那我们把最后这个叫做y real。一样把它显示出来看看。
可以看到最后模型的预测呢,是跟我们说可以给这个人6.55*10的一次方这样子的薪水,也就是65.5k。那如果今天又有第二个人来面试了,他告诉你他的工作经验是7.2年,然后他是高中以下的学历,那工作的地方呢,在城市B好了。一样我们如果想要预测一下可以给这个人多少薪水,我就把这一笔资料也把它加进来。直接在这边再多写一笔,它是7.2,然后高中以下对我们来说就是0,转换之后是0。城市b呢,转换之后就是0, 1。OK,这是第二个人。我们一样做同样的预测。执行。
可以看到模型给出的预测,就是说可以给这个人23.4k的薪水。那这个就是怎么样把模型套用到真实的情况上。
在上一步我们已经做完了gradient descent。其实呢,在我们这个例子,我们可以加速gradient descent的。只要利用一个小小的技巧,叫做特征缩放,英文是feature scaling,它就可以让我们达到加速的效果。
好,我们先看一下。以现在这个例子有四个特征嘛,分别是年资、学历还有工作的地方cityA跟cityB。然后我们是以多元线性回归去预测他的月薪。所以写起来会长这样,W1*X1+W2*X2加上3加上4,最后加上b。这边的X1234呢,就是我们的这四个特征。那我们从这四个特征的值可以发现,它们的分布是不一样的。以第一个特征来说,它的分布范围呢,大概是1到10之间。然后第二个特征呢,是0到2。第三跟第四个特征呢,不是0就是1。所以他们分布的范围是这样子。我们可以很明显的看出来,x1这个特征它的分布范围是大于其他这三个特征的。
所以我们把它带回原本的这个式子里面,就会长这样。我们w1是乘以x1,所以呢,它是乘以一个相对比较大的值,然后W234呢,都是乘以相对比较小的值。只要这边的大小它的差距越大的话,就让我们的gradient descent越慢,也就是让我们的梯度下降越慢。为什么呢?因为你看这边W1,它是乘以一个比较大的值,然后W234都乘比较小的值。也就是说这个W1,它只要稍微有一点点的变动,就会很大的影响这边计算出来的结果,因为他是乘以一个比较大的值嘛。那它很大的影响这边计算出来的结果,也就间接的影响计算出来的cost。也就是说w1它只要稍微有一点点变动,计算出来的cost也会变动很大。
所以这边如果我们拿W1跟W2来比较他们相对应的cost的话,大概会长这样。这里我用一个等高线图来做比较,中心点的地方是cost最低的地方,越往外圈cost越高。然后x轴是W1,y轴是W2。我们可以看到这个等高线图它是一个狭长型的,为什么呢?因为这边W1,它是乘以一个相对较大的值,所以W1它只要有一点点的变动,就会很大的影响这边cost的值。我们说了最低点呢,是这个红点的地方,然后越往外圈是cost越大。这边可以看得到W1只要有一点点变动,这个cost的变动就会很大。相反的,W2的变动就不怎么样影响cost的值。
那我们来看一下,在这种情况下做gradient descent会发生什么事情呢?假设我们的初始点是在这里,初始的w1 2是在这里,我们做gradient descent参数的更新,就很有可能发生这样子的状况。可以发现它在这里来回震荡。为什么呢?因为我们说了,w1它只要稍微有一点变动,就会很大的影响cost。那我们这边在做gradient descent更新W1的时候,就很有可能一不小心就更新过头了。所以呢,这边不小心更新过头了,就到这里,又不小心更新过头了,又不小心更新过头了,就会导致这样来回的震荡。那这样子呢,就会让我们到最低点的速度变得很慢,也就是我们的gradient descent会变得很慢。
那我们要怎么样解决这个问题呢?非常的简单。会造成这个问题的原因就是特征的大小范围不一样嘛,我们这边第一个特征的范围比较大,然后其他比较小,所以才会造成这个问题嘛。想要要解决的话,我们就把特征的范围都变成同样的就可以了。我先把这边的大也变成小。我们来看一下四个特征都在同一个范围之后,我们的等高线图就会变成这个样子。那我们现在在做gradient descent的话,就会非常的顺利,直接呢走到最低点的地方。
所以呢,我们只要把每一个特征它的大小缩放到同一个范围内,就可以加速我们的gradient descent。要做特征缩放有很多种方式,这边我就介绍一个很经典很常用到的,叫做标准化,英文是standardization。他的做法呢,是把我们的特征,去减掉这个特征的平均,再除以这个特征的标准差。以我们的年资特征来说的话,就是把年资的每一笔资料都扣掉年资的平均,再除以年资的标准差。这边如果你不知道什么是标准差也没关系,因为有很多工具都可以帮我们自动去做计算。
所以如果我们把每一个特征,都经过标准化之后就会变成这个样子。可以看到他们的大小范围呢,就会都非常的接近。这边你可能会有疑问说,我们后面这3个特征不是本来就很小了嘛,为什么还要做特征缩放呢?没有错,他们本来就很小了,所以如果你要做跟不做都是可以的。这边我就全部都把它做缩放了。
接着呢,我们就直接来实作特征缩放啦。要做特征缩放的话,我们是在分完测试集跟训练集之后才做特征缩放。我们分测试集跟训练集是在这里嘛,所以呢,在他分完之后我们才做特征缩放。这边我们要做特征缩放的方法是标准化嘛,要用标准化的话,我们可以直接引入sklearn,它底下的prepossessing,然后呢,再去引入standard scaler。那我们可以这样使用它,因为它是一个类别,所以我先把它做创建,把它叫做scaler。创建完之后呢,我们再让他去看过训练集的特征资料。注意哦,这边只能让他看训练集的特征资料,不能让他看测试集的特征资料,因为测试集的资料我们只有在做测试的时候才能使用到。这边我们可以写点fit,然后把x train把它传进来。这样传进来之后,它就会计算这里面特征的平均、标准差等等的。好,那它计算完之后,我们就可以把它做转换啦。要做转换我们可以写点transform,然后一样把x train把它传进来。这样它就会做转换了。我们把转换的结果直接替代掉原本的。好,那我们把它显示出来看看。
这就是转换之后的结果。那这里我顺便把x test也把它做个转换。这里我们只是先做转换,没有要使用它。这样之后再做测试的时候会比较方便。那一样这边我们就是把转换之后的结果直接替代掉。这边特别注意一下,我们不会再另外根据测试集的资料,去计算他的标准差啊,计算他的平均,然后再做转换。我们会直接使用训练集拟合出来的结果,也就是这边训练集所计算出来的标准差跟平均,就会直接使用到测试集上面来做转换。那一样我直接把它显示出来看看。这个呢,就是测试集转换出来的结果。
接着我们直接来比较一下,经过特征缩放之后我们再去做gradient descent,跟没有做特征缩放的gradient descent,它差别在哪。我们之前做的gradient descent它在这边,这个是结果,我直接复制一份。在这里再重新执行一次。这里所有的参数都是一样的,唯一不同的呢,是我们的x train经过了特征缩放。那我在全部都相同的情况下再执行一次,来看一下是不是真的有比较快。
可以看到大概在1,000次的更新的时候,它的cost呢,就到2.5*10的一次方了。然后下面2,000,3,000次的更新,基本上cost都没有动了。所以呢,我们可以猜测,它差不多是已经到达了最低点的地方。我们对比一下之前的结果,之前一直更新更新更新到了差不多4000次左右,他才到2.5*10的一次方。所以呢,我们很明显的可以看得出来,经过特征缩放之后,确实我们在做gradient descent,他下降的速度有比较快。
接下来跟大家介绍,我们在做分类问题的时候非常好用的一个模型,叫做逻辑回归,英文是logistic regression。先来了解一下今天我们想要解决的问题。假设我们知道一个人有没有得糖尿病,可能跟他的年纪、体重、血糖还有性别有关系。所以呢,我们就去收集了一些资料。这边的每一笔资料就代表一个人,他的年纪、体重、血糖还有性别以及他有没有得糖尿病。1的话就表示他有得糖尿病,0的话就是没有。所以呢,我们就是想要根据这4个特征,去预测这个人他有没有得糖尿病。那有没有得糖尿病输出的值只有两种可能嘛,要么是1,要么是0,也就是有得糖尿病跟没有得糖尿病。这样就跟我们前两个例子差很多了嘛,我们前两个例子,在做简单线性回归跟多元线性回归的时候,预测的是月薪。那月薪它输出的可能就有无限多种,不像这边输出的可能只有两种,要么有得糖尿病,要么没有得。这种输出的可能性是有限的,我们就会说它是一个分类问题,英文是classification。
这边稍微打个预防针,这些资料都是我随机产生的,所以如果你有医学背景的人不用太认真。然后你可能也会说,不是血糖超过多少就有得糖尿病吗?一样,我们先当作这些我们都不知道。
好,接着用图的方式来看一下。这是之前线性回归的例子,我们想要用年资去预测薪水。那其实特征的地方呢,可以不只有年资,还可以有工作的地方、学历等等的。这边是为了图方便呈现,所以我只显示年资。那我们说可以用一条线来表示这些资料嘛。那回到我们今天的这个例子,我们想要用一个人的血糖去预测他有没有糖尿病。这边的特征一样可以有很多个,可以有刚刚说的体重、年纪、性别等等的。一样这里我为了方便图的呈现,所以我只画一个血糖的特征。我们可以看到这边资料的分布就跟左边差很多了嘛,因为这边它的输出值只有两种可能,要么是1,要么是0,也就是有糖尿病跟没有糖尿病。所以呢,看起来就会长这样。
那这个时候你还觉得我们要用一条线来表示这些资料嘛?好像不太对吧。那怎么办呢?其实我们也不用做太大的调整,只要把这条线稍微弯曲一下变成这个样子,是不是就好像可以表示这些资料了嘛?这条弯曲的线它的值只可能在0到1之间,那就跟我们的资料非常的吻合,因为我们的资料它不是1就是0。
那现在的问题就会变成我们要怎么样把它做弯曲呢?非常的简单,我们只要透过一个函数叫做sigmoid function,中文的话可以把它叫做s型函数,就可以达到弯曲的效果了。以我们之前线性回归的例子来说,我们数学式子可以写成这样y=w*x+b,或者你有很多特征的话,我们可以写成y=W1*X1+W2*X2一路加上去看你有几个特征。如果我们想要把这样子线性的模型做弯曲的话,只要把它带入sigmoid function就可以了。那这个sigmoid function长这样,它是1/1+e的一个次方。念起来有点绕口令的感觉哦。那我们只要把这个线性的模型呢,并且加上负号它带到次方这边就可以做弯曲了。并这边你可能会有一个疑问,就是这个英文字母的e是什么意思呢?这个e它只是一个常数,代表的是2.7182818.... 跟我们的数学圆周率π一样,它只是一个常数。圆周率π就是3.1415926.... 嘛。所以我们只要把这边这一串,把它带到次方的这个位置并且加上负号,就可以达到弯曲的效果了。那在带入之前呢,我先把这边的y把它做个改变,我把它改成z。OK,改成z。那带入进去之后呢,我们新的预测值y,就会变成这个样子。把这一串带到里面来看你有几个特征,这边就是乘几个。这个呢,就是我们逻辑回归的模型。大家也不要看他长这个样子就觉得很可怕,其实有很多工具都可以帮我们自动的做计算。
回到我们想要解决的问题。今天我们是想要用年纪、体重、血糖还有性别,来预测有没有糖尿病。所以呢,我们的特征就有这4个。也就是说我们想要找出一组最佳的W1234以及b的组合,让它带入这边的Sigmoid function做了弯曲之后,可以最能表示我们的资料。
那接着我们就直接来实作啦。首先我们一样先把资料读取进来。读取的动作跟之前都是一样的,所以我直接用复制的。不过这边资料的网址是不一样的,所以我把它改一下。那这个网址呢,一样在课程档案里面你可以找得到。好,那我们执行它。
可以看到这就是我们的资料,总共有0-399,所以有400笔资料。读取进来之后,我们先把资料做个处理。这边有文字嘛,文字的部分我们要先把它转换成数字。这里我就把男生转换成0,女生转换成1。所以呢,我们去取得它的gender,也就是性别这个特征,然后呢,我去把它做个转换。我们可以用map把它做转换,是男生对应到1,女生对应到0。那我们把它转换完之后,再把它显示出来看看。可以看到这样就转换完成了。
接着我们就来把资料分成训练集跟测试集。那分法跟之前都是一样的,所以我就直接用复制的。OK,在这边,直接复制过来。不过这边的x跟y呢,要改一下。我们的特征是age,然后是体重...y的部分是有没有糖尿病。那这样分完之后呢,我把它显示出来看看。把x train跟x test显示出来看看。OK,这个就是分完的结果。那我们也把y的部分也显示出来看看。y train跟y test。OK,没有问题。这边我们一样是测试集用0.2,也就是2成。那我们总共有400笔资料嘛,所以2成就会是80笔测试集,320笔是训练集。
接着因为我们的特征有4个嘛,那他们分布的范围又都不一样。如果我们想要让他在往后的gradient descent跑得比较快的话,那我们需要做个特征缩放。特征缩放的部分跟之前都是一样的,所以一样我直接用复制的。OK,特征缩放在这里。直接复制过来。那这边呢,我们一样把缩放的结果显示一下。可以看到这个就是缩放之后的结果。我把x test也显示一下。OK,没有问题。
资料处理的部分都完成了之后,我们就可以把它带入到模型里面啦。首先我先随便设定w跟b的值。那这边呢,我先把numpy把它引入进来。w的话我就让它是里面有4个值的阵列,因为我们有4个特征嘛。OK,有4个特征,W1234我就让它是1234。接着还要设定一个b,b的话我就让它是1。
那我们的模型呢,是这样,先让w去乘以x。我们现在训练阶段,所以我乘以x train。好,把它的结果显示出来。这是乘完之后的结果。乘完之后我们要把它做相加嘛,这跟之前都是一样的,所以我们用sum,然后让它是轴线1的部分做相加。再执行。好,这是相加之后的结果。加完之后我们要让它加上b。那这样子呢,这一串就会是我们之前多元线性回归的时候预测的y。那我们现在是要用逻辑回归,所以呢,这个我把它叫做z。
接着我们就是要把这个z,把它带到sigmoid function里面。我就先来做sigmoid function,我把它叫做就叫做sigmoid,然后它传入一个z。计算的方式就是1去除以1加上一个英文字母e的某个次方嘛。所以呢,英文字母的e这边可以写np.exp,然后他要几次方呢,就是写在这个小括号里面。那我们想要的是-z次方。好,就把它回传。这边呢,分母的地方要括号起来。那我把这个numpy的引入把它写到这里。OK,执行。
接着我们就直接来使用它。我们就把这个z把它带进去看一下。经过转换之后可以看到,经过这个simoid function之后呢,里面的每个值它都会在0到1之间。所以呢,现在我们已经成功的把这个线性的模型,这边本来是多元线性回归嘛,我们已经把这个线性的模型做了弯曲。接着呢,一样,我们就是要找出一组最佳的w跟b的组合,让它最能表示我们的资料。