逻辑回归
逻辑回归用于二分类问题,给定一个输入特征向量X,输出一个预测值y ^^^ ,来告诉我们y=1的概率。
假设X是一个nx维向量(相当于有nx个特征的特征向量),我们用w表示逻辑回归的参数,这也是nx维的向量,还有一个参数b,这是一个实数,所以y ^^^=w T x+b.
但是上述是一个关于X的线性函数,对于二分类问题不可行,因为我们想让y ^^^ 表示概率,那么它的取值范围[0,1],因此需要将y^^^ 带入simoid函数中。
sigmoid函数表达式为:
图像如下:

由图可以看出:当x很大时,输出接近于1,当x很小时,输出接近于0,满足我们二分类的要求。
逻辑回归的损失函数
损失函数又叫做误差函数,用来衡量算法的运行情况,来衡量预测输出值和实际值有多接近。一般我们用预测值和实际值的平方差或者它们平方差的一半,但是通常在逻辑回归中我们不这么做,因为当我们在学习逻辑回归参数的时候,会发现我们的优化目标不是凸优化,只能找到多个局部最优值,梯度下降法很可能找不到全局最优值,虽然平方差是一个不错的损失函数,但是我们在逻辑回归模型中会定义另外一个损失函数。
我们在逻辑回归中用到的损失函数是:
我们的目标是使得损失函数尽可能小,
当y=1,L=-log(y^^),L尽可能小,也就是y^^ 尽可能大,也就是y^^^ 越接近y
当y=0,L=-log(1-y^^^) ,L尽可能小,也就是y^^^ 尽可能小,也就是y^^^ 越接近y
损失函数是在单个训练样本中定义的,它衡量的是算法在单个训练样本中表现如何
为了衡量算法在全部训练样本上的表现如何,我们需要定义一个算法的代价函数,算法的代价函数是对m个样本的损失函数求和然后除以m:

损失函数只适用于像这样的单个训练样本,而代价函数是参数的总代价,所以在训练逻辑回归模型时候,我们需要找到合适的w和b,来让代价函数 J的总代价降到最低。
梯度下降法
思想:不断迭代,从而找到最好的参数
假设代价函数的图像如下:

-
可以用如图那个小红点来初始化参数w和b,也可以采用随机初始化的方法,对于逻辑回归几乎所有的初始化方法都有效,因为函数是凸函数,无论在哪里初始化,应该达到同一点或大致相同的点。
-
朝最陡的下坡方向走一步,不断地迭代(a为学习率,也就是步长)

- 直到走到全局最优解或者接近全局最优解的地方
通过以上的三个步骤我们可以找到全局最优解,也就是代价函数(成本函数)J(w,b)这个凸函数的最小值点。
计算图
一个神经网络的计算,都是按照前向或反向传播过程组织的。首先我们计算出一个新的网络的输出(前向过程),紧接着进行一个反向传输操作。后者我们用来计算出对应的梯度或导数。计算图解释了为什么我们用这种方式组织这些计算过程。
例如:

从左往右计算J的值,从右往左计算导数。
使用计算图求导数
实际上就是链式求导
已知:
求:

逻辑回归中的梯度下降


假设只有一个样本,则代价函数如下:(a就是预测值,y是真实值)
画出计算图:

求偏导:

更新参数:

对于m个样本的逻辑回归
代价函数:
求偏导:
若只有一个样本,则dw就是一个样本求偏导;若有m个样本,则dw就是m个样本的偏导的平均。

最后更新参数:

至此一轮迭代结束。
向量化
向量化是非常基础的去除代码中 for 循环的艺术,在在深度学习领域,运行向量化是一个关键的技巧。
在逻辑回归中,我们需要计算z=wT x + b,其中w,x都是列向量,因此如果用非向量化方法计算z,代码如下:
import numpy as np
import time
x = np.random.rand(100000)
w = np.random.rand(100000)
b = np.random.rand()
z = 0;
for i in range(10000):
z += x[i] * w[i]
print(z)
向量化的方法,代码如下:
import numpy as np
import time
'''
dot(a,b):
当a和b是一维数组时,做点乘
当a和b是多维矩阵时,做矩阵乘法
a*b:对于位置元素相乘
'''
x = np.random.rand(100000)
w = np.random.rand(100000)
b = np.random.rand()
# np.dot(a,b):a和b做点乘
z = np.dot(x, w)+b
print(z)
现在做一个测试,看看哪个方法速度更快
import numpy as np
import time
x = np.random.rand(100000)
w = np.random.rand(100000)
b = np.random.rand()
z = 0;
s1 = time.time()
for i in range(100000):
z += x[i] * w[i]
z += b
e1 = time.time()
print("非向量化的运行时间:" + str(1000 * (e1 - s1)) + "ms")
s2 = time.time()
z = np.dot(x, w) + b
e2 = time.time()
print("向量化的运行时间:" + str(1000 * (e2 - s2)) + "ms")
输出:
事实上,可以通过python的内置函数来替换for循环
逻辑回归的实现
假设X是n维的
原始版本的实现:需要两个for循环,十分低效

设:

那么:
一轮迭代结束。
import numpy as np
def sigmoid(x):
# 直接返回sigmoid函数
return 1. / (1. + np.exp(-x))
# 初始化
m = 100
n = 10
a = 0.001
iteration = 10000
X = np.random.randint(10, size=(n, m))
Y = np.random.randint(2, size=(1, m)) # 随机生成不大于2的n*1维矩阵
b = np.random.rand()
W = np.random.rand(n, 1)
# 实现
for i in range(iteration):
Z = np.dot(W.T, X) + b #矩阵乘法
A = sigmoid(Z)
dZ = A - Y
dW = np.dot(X, dZ.T) * (1 / m)
db = np.sum(dZ) * (1 / m)
loss = -(Y * np.log(A) + (1 - Y) * np.log(1 - A)) # 矩阵点乘
loss = np.sum(loss) / m
print(loss)
W -= a * dW
b -= a * db
Python中的广播
广播就是在水平方向上复制或者在垂直方向上复制,以得到符合要求的矩阵
import numpy as np
'''
A是一个3*4的矩阵
'''
A = np.array([[56, 0, 4.4, 68],
[1.2, 104, 52, 8],
[1.8, 135, 99, 0.9]])
cal = A.sum(axis=0) # axis=0:每一列求和,axit=1:每一行求和
print(cal)
输出:[ 59. 239. 155.4 76.9]
'''
A是3*4的矩阵,cal是1*4的矩阵,不能直接相除,
所以cal在垂直方向复制3次,变成3*4的矩阵,
然后对应位置元素相除
'''
percentage = 100 * A / cal.reshape(1, 4)
print(percentage)
输出:[[94.91525424 0. 2.83140283 88.42652796]
[ 2.03389831 43.51464435 33.46203346 10.40312094]
[ 3.05084746 56.48535565 63.70656371 1.17035111]]
关于numpy的注意点
当创建一个行向量时
a = np.random.randn(1, 5)
当创建一个列向量时
a = np.random.randn(5, 1)
打印矩阵的维数
a = np.random.randn(1, 5)
print(a.shape)
矩阵转置
a = np.random.randn(1, 5)
a = a.reshape(5, 1)
numpy用法总结
指数函数
'''
np.exp(x)
x可以是一个数,也可以是一个矩阵
返回e的x方
'''
import numpy as np
x = np.array([1, 2, 3])
print(np.exp(x))
输出:[ 2.71828183 7.3890561 20.08553692]
sigmoid函数
函数原型
sigmoid(x)=1/(1+exp(-x))
导数
s=sigmoid(x)
ds=s*(1-s)
reshape
# 如果将v的shape(a,b,c),转为(a*b,c),v.shape[0] = a ; v.shape[1] = b ; v.shape[2] = c
v = v.reshape((v.shape[0]*v.shape[1], v.shape[2]))
归一化
np.linalg.norm(x,ord=None,axis=None,keepdims=False)
```
x:矩阵,可以是一维的
ord:范数类型,默认二范数,
ord=2 二范数
ord=1 一范数 |x1|+|x2|+|x3|+...+|xn|
ord=np.inf 无穷范数 max(|xi|)
axis=0:按列处理
axit=1:按行处理
keepdims:表示是否保持矩阵的二位特性,True表示保持,False表示不保持,默认为False
```
通过归一化,可以使得梯度下降的更快
# GRADED FUNCTION: normalizeRows
def normalizeRows(x):
x -- A numpy matrix of shape (n, m)
norm=np.linalg.norm(x,axis=1) # norm是n*1,不过是一维的
x=x/norm.reshape(x.shape[0],1) # 按行归一化,所以x的每一个元素除与所在行的norm,涉及到广播
return x
x = np.array([
[0, 3, 4],
[1, 6, 4]])
print("normalizeRows(x) = " + str(normalizeRows(x)))
输出:normalizeRows(x) = [[0. 0.6 0.8 ]
[0.13736056 0.82416338 0.54944226]]
np.sum()
np.sum(a, axis=None, dtype=None, out=None, keepdims=np._NoValue)
axis=0 按列
axit=1 按列
keepdims =True 将保持a的维度
def softmax(x):
x_exp=np.exp(x)
rowSum=np.sum(x_exp,axis=1).reshape(x.shape[0],1)
s=x_exp/rowSum
return s
softmax表达式:
向量化
np.outer()
函数作用:通常用来计算两个向量的外积,第一个数组的每一个元素×第二个数组的各个元素
函数调用方法:
np.outer(a,b,out=None)
a:第一个输入向量,假设维度(M,1)
b:第二个输入向量,假设维度(N,1)
out:设置结果的保存位置,通常使用默认值None,不需要做多余处理。
有返回值,返回一个维度为(M,N)的ndarray数组。
np.multiply(a,b)
对应元素相乘
np.dot(a,b)
(1)如果a和b都是一维的,那么结果就是普通的內积(inner product)。
(2)如果a和b都是二维的,运算等同于矩阵乘法(Dot product / matrix product)。

(3) 矩阵x为m×n阶,向量y为n阶向量,则矩阵x和向量y可以进行乘法运算,结果为m阶向量。进行运算时,会首先将后面一项进行自动转置操作,之后再进行乘法运算。
import numpy as np
x = np.array([[1, 2, 3],
[3, 4, 4]])
y = np.array([1, 2, 3])
result = np.dot(x, y)
print(result)
print("x阶数:" + str(x.shape))
print("y阶数:" + str(y.shape))
print("result阶数:" + str(result.shape))
输出:
[14 23]
x阶数:(2, 3)
y阶数:(3,)
result阶数:(2,)
numpy.squeeze()函数
作用:从数组的形状中删除单维度条目,即把shape中为1的维度去掉
#例1
import numpy as np
a = np.arange(10).reshape(1,10)
a
->array([[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]])
a.shape
->(1, 10)
b = np.squeeze(a)
b
->array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
b.shape
->(10,)
#例2
c = np.arange(10).reshape(2,5)
c
->array([[0, 1, 2, 3, 4],
[5, 6, 7, 8, 9]])
np.squeeze(c)
->array([[0, 1, 2, 3, 4],
[5, 6, 7, 8, 9]])
综上可知,np.squeeze()函数可以删除数组形状中的单维度条目,即把shape中为1的维度去掉,但是对非单维的维度不起作用。
np.zeros()
numpy.zeros(shape, dtype=float)
shape:创建的新数组的形状(维度)。
dtype:创建新数组的数据类型。
返回值:给定维度的全零数组。
import numpy as np
array = np.zeros([2, 3])
print(array)
print(array.dtype)
"""
result:
[[0. 0. 0.]
[0. 0. 0.]]
float64
"""
plt
1.plt.legned
plt.legend(loc,)
loc:
'best' : 0, 自适应方式
'upper right' : 1,
'upper left' : 2,
'lower left' : 3,
'lower right' : 4,
'right' : 5,
'center left' : 6,
'center right' : 7,
'lower center' : 8,
'upper center' : 9,
'center' : 10
2.plt.figure
plt.figure(figsize=(14, 6), dpi=80)#设置绘图区域的大小和像素
3.plt.xticks
plt.xticks(new_year)#设置x轴的刻度线为new_year,new_year可以为数组
4.plt.xlabel
plt.xlabel('year')#x轴标签
5.plt.plot
plt.plot(number, color='blue', label="actual value")#将实际值的折线设置为蓝色
6.两个图分开
fig, axes = plt.subplots(2, 1, sharex=true,figsize=(10,10))
axes[0].plot(range (len(data20)),data20,'r')
axes[1].plot(range (len(data40)),data40,'b')
7、画竖直线
plt.axvline(99, linestyle="dotted", linewidth=4, color='r')#99表示横坐标
8、图片保存
plt.savefig('timeseries_y.jpg')
转置技巧
假设一个数组a.shape(x,y,z)
如果将它转成(y*z,x)
笨方法:
a=a.reshape(a.shape[1]*a.shape[2],a.shape[0])
简单方法
a=a.reshape(a.shape[0],-1).T
预处理的三个通常步骤
- 计算出数据集的维度
- reshape数据集
- 标准化数据集