Logistic Regression在图像识别的实现

文章目录
  1. 1. 学习普通算法的模型结构
  2. 2. 建立算法
  3. 3. 帮助函数
  4. 4. 初始化参数
  5. 5. 前向和反向传播
  6. 6. 优化
  7. 7. 预测结果
  8. 8. 合并所有的功能到一个模型中
    1. 8.1. 图片分类示例
    2. 8.2. 绘制损失函数曲线
  9. 9. 特征分析
    1. 9.1. 学习率选择
    2. 9.2. 使用其他图片进行预测

这篇博客是基于吴恩达老师深度学习与神经网络课程第二周的作业来写的,第二周的学习笔记链接:http://klausvon.cn/2019/08/10吴恩达神经网络与深度学习-Week-2-学习记录/

第二周的主要内容就是Logistic Regression,其中涉及到的代价函数,损失函数,预测等在第二周的作业中都有对应的代码实现,博客主要参考作业文档:https://github.com/AlbertHG/Coursera-Deep-Learning-deeplearning.ai/tree/master/01-Neural%20Networks%20and%20Deep%20Learning/week2

作业中给的数据格式是.h文件,数据链接和课程中预置的数据处理.py文件都可以在github链接中看到,吴老师课程里使用的是Jupyter,我使用Pycharm进行代码编写。

关于下面这行代码:

1
classes[int(np.squeeze(my_predicted_image)),].decode("utf-8")

中间一定要加上int()来整数化,否则会报错,原文中有一个地方没加这个函数,代码出现了报错。

学习普通算法的模型结构

建立一个Logistic Regression,使用神经网络来识别一张图片上的物种是不是猫。

当需要识别一张图片中是不是猫的时候,需要以下关键步骤:
1.初始化模型中的参数;
2.通过最小化代价函数(Cost Function)来学习参数;
3.使用学习后的参数来进行预测;
4.分析结果和得出结论。

建立算法

构建一个神经网络的主要步骤如下:
1.设计模型结构(比如输入的特征值);
2.初始化模型的参数;
3.循环:
·计算当前损失(Loss Function);
·计算当前梯度;
·更新参数;
构建以上1-3个函数,并将他们集成到模型中。

帮助函数

构建函数实现 sigmoid 计算,通过计算 sigmoid(w.T*x+b)来实现。

1
2
3
4
5
6
7
8
9
# construct sigmoid function
def sigmoid(z):
s=1./(1.+np.exp(-z))
return s

# test sigmoid function
print("sigmoid(0)=",sigmoid(0))
print("sigmoid(9.2)=",sigmoid(9.2))
print('-----------------')

初始化参数

初始化参数,w 初始化为0向量。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
def initialize_with_zeros(dim):
w=np.zeros((dim,1))# 生成(dim,1)大小的全0矩阵
b=0
# trick:确认生成的w向量大小为(dim,1)
assert(w.shape==(dim,1))
assert(isinstance(b,float) or isinstance(b,int))

return w,b

dim=2
w,b=initialize_with_zeros(dim)
print("w=",w)
print("b=",b)
print('-----------------')

前向和反向传播

现在,经过上述步骤,参数已经初始化过了,可以通过前向传播和反向传播来学习参数。
前向传播:
1.已经拥有输入的X;
2.计算A=σ(w.Tx+b)=(a(0),a(1),a(2),…,a(m-1),a(m))
3.计算代价函数:

也许上述描述不够清晰,可以参看原文:

前向传播中使用到的公式(第一个式子其实就是dw的值,第二个式子就是db):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
# 实现梯度函数
def propagate(w,b,X,Y):
'''
:param w: 权重,大小为(num_px*num_px*3,1)
:param b: 偏置值
:param X: 数据量大小,(num_px*num_px*3,样本数)
:param Y: 真实的标签值,包含了两个值(0表示non-cat,1表示cat),大小为(1,样本数)
:return:
cost -- 逻辑回归中损失的log值
dw -- 损失(loss)对w的梯度,大小与w相同
db -- 损失(loss)对b的梯度,大小与b相同
'''

# 根据吴老师课程中提到的,样本数使用列(col)来表示
m=X.shape[1]

# 前向传播
A=sigmoid(np.dot(w.T,X)+b) # 计算激活函数
# 原文本中没有 +b 这一项,这是私人添加的
cost=(-1/m)*(np.sum(Y*np.log(A)+(1-Y)*np.log(1-A))) # 计算代价函数

# 反向传播
dw=np.dot(X,(A-Y).T)/m
# 这里的公式是直接用的,因为吴老师在课程里已经推导过了
db=np.sum(A-Y)/m

assert(dw.shape==w.shape)
assert(db.dtype==float)

# 删掉shape中1的维度
cost=np.squeeze(cost)

grads={"dw":dw,
"db":db}

return grads,cost

# 测试
w,b,X,Y=np.array([[1],[2]]),2,np.array([[1,2],[3,4]]),np.array([[1,0]])
grads,cost=propagate(w,b,X,Y)
print("dw=",grads["dw"])
print("db=",grads["db"])
print("cost=",cost)

优化

截至目前为止,已经完成了:
·已经初始化了参数;
·可以计算一个代价函数和它的梯度;
现在,需要使用梯度下降来更新参数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
#  梯度优化函数
def optimize(w,b,X,Y,num_iterations,learning_rate,print_cost=False):
'''
:param w: 权重,(num_px*num_px*3,1)
:param b: 偏置项
:param X: 输入数据,(num_px*num_px*3,样本数)
:param Y: 真实的标签值,包含了两个值(0表示non-cat,1表示cat),大小为(1,样本数)
:param num_iterations: 优化需要迭代的次数
:param learning_rate: 更新梯度下降时需要用到的学习率
:param print_cost: 输出每100次训练的cost
:return:
params -- 包含w和b的字典
grads -- 包含了w和b在代价函数中的梯度下降值
costs -- 列出优化过程中计算的所有成本,这将用于绘制学习曲线
'''

costs=[]
for i in range(num_iterations):
# 代价和梯度计算
grads,cost=propagate(w,b,X,Y)

# 从梯度中获取导数
dw = grads["dw"]
db = grads["db"]

# 更新w和b的值
w=w-learning_rate*dw
b=b-learning_rate*db

# 每100次训练后的cost值加入cost[]
if i%100 == 0:
costs.append(cost)

# 输出每100次训练后得到的值
if print_cost and i%100==0:
print("Cost after iteration %i: %f" %(i, cost))
params={"w":w,
"b":b}
grads={"dw": dw,
"db": db}
return params,grads,costs

# 测试
params, grads, costs = optimize(w, b, X, Y, num_iterations= 100, learning_rate = 0.009, print_cost = False)

print ("w = ",params["w"])
print ("b = ",params["b"])
print ("dw = ",grads["dw"])
print ("db = ",grads["db"])
print('-----------------')

预测结果

在上述的计算中已经得到了w和b,现在要用w和b来预测数据集X的标签值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 预测结果
def predict(w,b,X):
# 样本总数
m=X.shape[1]
Y_prediction=np.zeros((1,m))
w=w.reshape(X.shape[0],1)

# 计算A的值来预测当前图片是猫的可能性
A=np.dot(w.T,X)

for i in range(A.shape[1]):
# 将概率值a[0,i]转换为实际预测值p[0,i]
if(A[0,i]>0.5):
Y_prediction[0][i]=1
else:
Y_prediction[0][i]=0

assert(Y_prediction.shape==(1,m))

return Y_prediction

print("prediction=",predict(w,b,X))
print('-----------------')

合并所有的功能到一个模型中

注:
·Y_prediction是测试集的预测值
·Y_prediction_train是训练集的预测值
·w,costs,grads是优化之后的输出值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
def model(X_train,Y_train,X_test,Y_test,
num_iterations=2000,learning_rate=0.5,print_cost=False):
'''
:param X_train: 通过numpy数组设置的训练集,(num_px*numpx*3,m_train)
:param Y_train: 通过一个numpy数组表示的标签向量,(1,m_train)
:param X_test: 通过一个numpy数组设置的测试集输入(num_px*num_px*3,m_test)
:param Y_test: 通过一个numpy数组设置的Y向量(1,m_test)
:param num_iterations: 表示优化参数的迭代次数的超参数
:param learning_rate: 在优化中更新学习率的参数
:param print_cost: 输出每100次迭代中的cost值
:return:
d -- 模型中包含信息的字典
'''

# 通过0来初始化参数
w,b=initialize_with_zeros(X_train.shape[0])

# 梯度下降
parameters,grads,costs=optimize(w,b,X_train,Y_train,
num_iterations,learning_rate,print_cost=False)
w=parameters['w']
b=parameters['b']

# 预测 测试集/训练集 设置的样例
Y_prediction_test=predict(w,b,X_test)
Y_prediction_train=predict(w,b,X_train)

# 输出 train/test 的错误
print("train accuracy: {} %".format(100 - np.mean(np.abs(Y_prediction_train - Y_train)) * 100))
print("test accuracy: {} %".format(100 - np.mean(np.abs(Y_prediction_test - Y_test)) * 100))

d = {"costs": costs,
"Y_prediction_test": Y_prediction_test,
"Y_prediction_train": Y_prediction_train,
"w": w,
"b": b,
"learning_rate": learning_rate,
"num_iterations": num_iterations}

return d


tic=time.process_time()
num_iterations=5000
d=model(train_set_x,train_set_y,test_set_x,test_set_y,
num_iterations=num_iterations,learning_rate=0.005,print_cost=True)
toc = time.process_time()
print('Use num_iterations of %i, run %f sec ' %(num_iterations, toc - tic))

图片分类示例

1
2
3
4
5
6
# 图片分类示例
index = 1
#plt.imshow(test_set_x[:,index].reshape((num_px, num_px, 3)))
print ("y = " + str(test_set_y[0,index]) + ", you predicted that it is a \""
,classes[int(d["Y_prediction_test"][0,index])].decode('utf-8') , "\" picture.")
#plt.close()

绘制损失函数曲线

1
2
3
4
5
6
costs = np.squeeze(d['costs'])
plt.plot(costs)
plt.ylabel('cost')
plt.xlabel('iterations (per hundreds)')
plt.title("Learning rate =" + str(d["learning_rate"]))
plt.show()

结果如下:

特征分析

学习率选择

注意:为了使梯度下降能更好的工作,必须明智的选择学习率。学习率α决定了更新参数的频率。如果学习率过大,我们可能会“超调”最优值。同样,如果它太小,我们将需要太多的迭代来收敛到最佳值。这就是为什么使用良好的学习速度是至关重要的。
比较以下几个学习率曲线来观察不同学习率下模型的效果。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 测试设置不同学习率在模型上的表现
learning_rates = [0.01, 0.001, 0.0001]
models = {}
for i in learning_rates:
print ("learning rate is: " + str(i))
models[str(i)] = model(train_set_x, train_set_y, test_set_x, test_set_y, num_iterations = 1500, learning_rate = i, print_cost = False)
print ('\n' + "-------------------------------------------------------" + '\n')

for i in learning_rates:
plt.plot(np.squeeze(models[str(i)]["costs"]), label= str(models[str(i)]["learning_rate"]))

plt.ylabel('cost')
plt.xlabel('iterations')

legend = plt.legend(loc='upper center', shadow=True)
frame = legend.get_frame()
frame.set_facecolor('0.90')
plt.show()

结果如下:

图像表示如下:

解释:
·不同的学习率会有不同的cost值,因此,预测结果也不一样;
·如果学习率过大,cost会不断震荡,甚至偏离;
·较低的cost并不意味着更好的模型,你必须检查是否存在过拟合现象。过拟合一般发生在training accuracy 过分过于 test accuracy 的情况上;
·深度学习中的建议:
-有效的选择学习率能更好的小化cost;
-如果模型过拟合了,使用其他技术来解决过拟合(后续课程中解释->后续博客中会写清)

使用其他图片进行预测

用来进行预测的图片:

1
2
3
4
5
6
7
8
9
10
11
12
# 使用其他图片进行测试
my_image = "my_image.jpg"

fname = "images/" + my_image
image = np.array(ndimage.imread(fname, flatten=False))
my_image = scipy.misc.imresize(image, size=(num_px,num_px)).reshape((1, num_px*num_px*3)).T
my_predicted_image = predict(d["w"], d["b"], my_image)

plt.imshow(image)
print("y = " + str(np.squeeze(my_predicted_image)) +
", your algorithm predicts a \"" +
classes[int(np.squeeze(my_predicted_image)),].decode("utf-8") + "\" picture.")

预测结果如下: