《吴恩达机器学习》5 Python/Numpy 教程(Octave/Matlab 教程)

前言

《吴恩达机器学习》是在2014年发布的(2011年录制),那个时候机器学习的编程语言用octave较多,但是现在几乎都是python的天下,几乎所有的机器学习框架都在python上有很好的实现方式,如TensorFlow,pytorch等。所谓磨刀不误砍柴工,学好python(不求精通)是很有必要的。另外值得提醒的有3点
1、以下的所有实现都是基于python3,若代码不通过请切换到python3。
2、安装python及最基本的使用请参考别的教程,这里只讨论与机器学习有关的python教程

一、python基本使用

我之前工作大部分使用的是cpp,cpp运行效率卓越但是同时开发效率也非常低。特别是现在开发接口的方式越来越拥抱调用restful接口的形式(可以查考阿里云,百度,科大讯飞的接口),而不是通过提供一种语言的SDK。我也用gunicorn+flask+gevent这种方式写过web接口,测试后发现性能非常好,一点也不比cpp用最底层的epoll这种形式差,现在我基本上能用python开发都不会用cpp。毕竟时间有限,特别是在学习新知识这款,不要纠结语言层的东西,能把算法应用上才是王道,而python非常友好,学习成本也低

1、数据类型

python中有number(包含了整型,浮点型),字符串,这两种使用和别的高级语言没有太多区别,我主要想讨论的是python中比较特殊的数据结构——列表(list)、元组(tuple)和dict(字典)
列表(list)
序列是Python中最基本的数据结构,用中括号 [ ] 表示。序列中的每个元素都分配一个数字 - 它的位置,或索引,第一个索引是0,第二个索引是1,依此类推。以下是一个列表

list1 = ['Google', 'Apple', 1997, 2000]
list2 = [1, 2, 3, 4, 5 ]
list3 = ["a", "b", "c", "d"]

元组(tuple)
Python 的元组与列表类似,不同之处在于元组的元素不能修改。用小括号 ( ) 表示。

tup1 = ('Google', 'Apple', 1997, 2000)
tup2 = (1, 2, 3, 4, 5 )
tup3 = ("a", "b", "c", "d")

字典(dict)
字典是另一种可变容器模型,且可存储任意类型对象。字典的每个键值(key=>value)对用冒号(:)分割,每个对之间用逗号(,)分割,用**花括号 { } ** 表示。

dict1 = {'Name': 'Tom', 'Age': 7, 'Class': 'First'}
dict2 = { 'abc': 123, 98.6: 37 }
print("dict1['Age'] = ", dict1['Age'])
print("dict2[98.6] = ", dict2[98.6])
# 以上代码输出
dict1['Age'] =  7
dict2[98.6] =  37

2、语法

python用四个空格区分代码结构,不像别的语言使用花括号。所以当你的代码逻辑嵌套太多就会非常乱,也有人调侃这个时候要带一把游标卡尺来量哪一行对应同个代码块,这个我猜是因为设计python的人鼓励你代码别写那么长,才这样设计的(逃~)
if语句

#!/usr/bin/python3
age = int(input("请输入你家狗狗的年龄: "))
print("")
if age < 0:
    print("你是在逗我吧!")
elif age == 1:
    print("相当于 14 岁的人。")
elif age == 2:
    print("相当于 22 岁的人。")
elif age > 2:
    human = 22 + (age -2)*5
    print("对应人类年龄: ", human)
### 退出提示
input("点击 enter 键退出")

while语句

#!/usr/bin/env python3
n = 100
sum = 0
counter = 1
while counter <= n:
    sum = sum + counter
    counter += 1
print("1 到 %d 之和为: %d" % (n,sum))

for语句

#!/usr/bin/python3
sites = ["you", "deserve","better","!"]
for site in sites:
    print("循环数据 " + site) # print中+表示链接字符串
print("完成循环1")
for num in range(5):
	print("循环数 ", num)	# print中,可以连接后面的number
print("完成循环2")

3、切片(重要

切片(Slice)也就是对list、tuple或者dict这种数据结构进行便捷的查询和修改方式,对于机器学习中经常操作训练数据的这种操作,切片是非常重要的,以下来看下传统的数据操作

# 传统中取前3个数据的如下
L = ['lisa', 'tom', 'jack', 'lucy', 'mike']
n = 3
for i in range(n):
	print(i)

对这种经常取指定索引范围的操作,用循环十分繁琐,因此,Python提供了切片操作符,能大大简化这种操作.对应上面的问题,取前3个元素,用一行代码就可以完成切片:

L = ['lisa', 'tom', 'jack', 'lucy', 'mike']
print(L[0:3])

L[0:3]表示,从索引0开始取,直到索引3为止,但不包括索引3。即索引0,1,2,正好是3个元素。如果第一个索引是0,还可以省略成L[:3]
以下是几种操作方式

L = ['lisa', 'tom', 'jack', 'lucy', 'mike']
print(L[0:3])	# 表示从索引0到索引3为止,及0, 1, 2
print(L[-2:])	# 表示从倒数第二个数到最后一个数
print(L[::2])	# 表示从所有的数中,每2个数取一个
print(L[:])		# 表示原封不动的同一个L

# 输出如下
>> ['lisa', 'tom', 'jack']
>> ['lucy', 'mike']
>> ['lisa', 'jack', 'mike']
>> ['lisa', 'tom', 'jack', 'lucy', 'mike']

4、函数及类

Python 定义函数使用 def 关键字,一般格式如下:

#!/usr/bin/python3
 
# 计算面积函数
def area(width, height):
    return width * height
 
def print_welcome(name):
    print("Welcome", name)
 
print_welcome("machine learning")
w = 4
h = 5
print("width =", w, " height =", h, " area =", area(w, h))

在Python中创建一个类和对象是很容易的,如下示

class Student(object):
    def __init__(self, name, score):
        self.name = name
        self.score = score
    def print_score(self):
        print('{}: {}'.format(self.name, self.score))
a = Student('jack', 98.5)
a.print_sore()
# 输出
>> jack: 98.5

二、numpy使用

前面是讲的都是python的数据结构和基本使用,而在机器学习下的运算都是使用numpy的(当然不同框架下也不是绝对的)。numpy使用起来也非常的方便,以下着重介绍下矩阵(多维数组)的运算

1、矩阵

numpy创建一个矩阵的方式非常方便,如下示

import numpy as np
a = np.array([[1, 2, 3], [4, 5, 6]])	# 创建一个2*3的矩阵
b = np.zeros((3,4))    					# 创建3*4全零二维数组
c = np.one((2,3,4))						# 创建2*3*4数值都是1的三维数组
d = np.zeros((5, 1))					# 创建5*1全零二维数组
e = np.identity(5)      				# 创建一个5*5的单位矩阵
# 输出如下
>>	[[1 2 3]
 	[4 5 6]]
>>	[[0. 0. 0. 0.]
 	[0. 0. 0. 0.]
 	[0. 0. 0. 0.]]
>>	[[[1. 1. 1. 1.]
  	[1. 1. 1. 1.]
  	[1. 1. 1. 1.]]

 	[[1. 1. 1. 1.]
  	[1. 1. 1. 1.]
  	[1. 1. 1. 1.]]]
>> 	[[0.]
 	[0.]
 	[0.]
 	[0.]
 	[0.]]
>>	[[1. 0. 0. 0. 0.]
 	[0. 1. 0. 0. 0.]
 	[0. 0. 1. 0. 0.]
 	[0. 0. 0. 1. 0.]
 	[0. 0. 0. 0. 1.]]

2、矩阵运算

矩阵的加减法运算可以直接用 + - 运算符计算,要求矩阵的维度一致,否则会报错

import numpy as np
a = np.array([[1, 2, 3], [4, 5, 6]])
b = np.ones(a.shape)
c = a + b
d = a - b
# 输出
>> a =	[[1 2 3]
 		[4 5 6]]
>> b = 	[[1. 1. 1.]
 		[1. 1. 1.]]
>> c = 	[[2. 3. 4.]
 		[5. 6. 7.]]
>> d =	[[0. 1. 2.]
 		[3. 4. 5.]]

矩阵的乘法

import numpy as np
A = np.array([[1, 1], [2, 2]])
B = np.array([[2, 0], [3, 4]])
C = np.array([[1, 1, 1], [2, 2, 2]])
D = A * B    		# 元素点乘
E = np.dot(B, C)   	# 矩阵乘法
>>	D = [[2 0]
		 [6 8]]
>>	E = [[ 2  2  2]
 		[11 11 11]]

矩阵的转置和逆矩阵,对于方阵A,如果为非奇异方阵,则存在逆矩阵inv(A)。对于奇异矩阵或者非方阵,并不存在逆矩阵,但可以使用pinv(A)求其伪逆

import numpy as np
a = np.array([[3, 3], [2, 2]])
b = np.linalg.pinv(a)
c = np.linalg.inv(a)
d = a.T
>> b = [[0.11538462 0.07692308]
 		[0.11538462 0.07692308]]
>> c = [[ 6.00479950e+15 -9.00719925e+15]
		[-6.00479950e+15  9.00719925e+15]]
>> d = [[3 2]
	 	[3 2]]

三、基于numpy的梯度下降法实现

上面讲了python和numpy的一些使用,那么我们来实现一个梯度下降法吧,如下示

import numpy as np
import matplotlib.pyplot as plt

# 从20-30 取199个点,并把x_data转为列向量
x_data_re = np.linspace(0, 30, 200)[:, np.newaxis]
x_data = (x_data_re - x_data_re.min()) / x_data_re.max() - x_data_re.min()
# 取概率分布的均值0,标准差为1的正太分布干扰变量
noise = np.random.normal(0, 0.1, x_data.shape)
# 设图像的斜率是2, 截距是6的图像
w = 2; b = 6
y_data =x_data*w + b + noise

# 下面开始用梯度下降法来得到我们的预期值 W 和 B
# 目标方程
def h(x_data, w, b):
    return w*x_data + b
# 代价函数
def J(x_data, y_data, w, b):
    return np.mean((h(x_data, w, b) - y_data)**2)/2
# 代价函数偏导数w
def dwJ(x_data, y_data, w, b):
    return np.mean((h(x_data, w, b) - y_data)*x_data)
# 代价函数偏导数b
def dbJ(x_data, y_data, w, b):
    return np.mean(h(x_data, w, b) - y_data)
print('初始损失率是 {}'.format(J(x_data, y_data, w, b)))

# 下面开始进行迭代,lr表示学习率,n_itera表示循环次数,epailon表示当损失函数变化很小时退出的阈值
def gradient_descent(x_data, y_data, w, b, lr=1e-3, n_itera = 1e4, epailon = 1e-8):
    w_history = list()
    b_history = list()
    W = w
    B = b
    w_history.append(W)
    b_history.append(B)
    itera = 0
    while itera < n_itera:
        # 保存上次的值
        print('此时的w = {},b = {}, 循环次数 = {}, 损失值 = {}'.format(W, B, itera, J(x_data, y_data, W, B)))
        lastW = W
        lastB = B
        # 计算梯度
        gradient_w = dwJ(x_data, y_data, W, B)
        gradient_b = dbJ(x_data, y_data, W, B)
        # 梯度下降迭代
        W = W - lr*gradient_w
        B = B - lr*gradient_b
        # 保存梯度下降的值
        w_history.append(W)
        b_history.append(B)
        if abs(J(x_data, y_data, W, B) - J(x_data, y_data, lastW, lastB)) <= epailon:
            print('此时的w = {},b = {}, 循环次数 = {}, 损失值 = {}'.format(W, B, itera, J(x_data, y_data, W, B)))
            break
        itera = itera+1
    return w_history, b_history

# 开始计算,给定初始值W,B
W = 0
B = 0
W_history, B_history = gradient_descent(x_data, y_data, W, B, lr=0.1)

# 画出训练集的点和我们希望的图像
plt.plot(x_data, y_data, 'bo')
plt.plot(x_data, x_data*W_history[-1] + B_history[-1], 'r-', lw=3)
plt.show()

下面就是训练的过程及拟合的图像
在这里插入图片描述
在这里插入图片描述

总结

由于《吴恩达机器学习》系列视频 Octave/Matlab 教程 中的octave已经不适合当前的形势了,故转为python/numpy教程,以便后续学习和查阅。

猜你喜欢

转载自blog.csdn.net/qq8993174/article/details/86592299