吳恩達(dá)深度學(xué)習(xí)筆記-改善神經(jīng)網(wǎng)絡(luò)(初始化嚎京、正則化嗡贺、梯度校驗(yàn))

在本周作業(yè)中,我們要做三件事鞍帝,也就是初始化诫睬、正則化、梯度校驗(yàn)帕涌。其中每個(gè)部分要做的事分別是:

1.初始化參數(shù):
(1)使用0來初始化參數(shù)
(2)使用隨機(jī)數(shù)來初始化參數(shù)
(3)使用抑梯度一場初始化參數(shù)
2.正則化模型:
(1)使用二范數(shù)對二分類模型正則化摄凡,嘗試避免過擬合
(2)使用隨機(jī)刪除節(jié)點(diǎn)的方法精簡模型,同樣是為了嘗試避免過擬合
3.梯度校驗(yàn):
(1)對模型使用梯度校驗(yàn)蚓曼,檢測它是否在梯度下降的過程中出現(xiàn)誤差過大的情況


完整代碼附后


準(zhǔn)備階段

首先導(dǎo)入初始庫:

import numpy as np
import matplotlib.pyplot as plt
import sklearn
import sklearn.datasets
import init_utils   #第一部分亲澡,初始化
import reg_utils    #第二部分,正則化
import gc_utils     #第三部分辟躏,梯度校驗(yàn)
#%matplotlib inline #如果你使用的是Jupyter Notebook谷扣,請取消注釋。
plt.rcParams['figure.figsize'] = (7.0, 4.0) # set default size of plots
plt.rcParams['image.interpolation'] = 'nearest'
plt.rcParams['image.cmap'] = 'gray'

初始化參數(shù)

初始化之前捎琐,先看一下數(shù)據(jù)集的形式:

讀取并繪制數(shù)據(jù)
train_X, train_Y, test_X, test_Y = init_utils.load_dataset(is_plot=True)
plt.show()
數(shù)據(jù)結(jié)果

我們要做的就是將圖片中的紅色藍(lán)色點(diǎn)分開会涎,我們用之前實(shí)現(xiàn)過的3層神經(jīng)網(wǎng)絡(luò),我們對它進(jìn)行初始化:
我們使用下列三種方式來對它進(jìn)行初始化:
(1)初始化為0瑞凑,在輸入?yún)?shù)中全部初始化為0末秃,參數(shù)名為initialization=“zeros”,核心代碼:

parameters['W' + str(l)] = np.zeros((layers_dims[l], layers_dims[l - 1]))

(2)初始化為隨機(jī)數(shù):把輸入?yún)?shù)設(shè)置為隨機(jī)值籽御,權(quán)重初始化為大的隨機(jī)值练慕。參數(shù)名為initialization = “random”惰匙,核心代碼:

parameters['W' + str(l)] = np.random.randn(layers_dims[l], layers_dims[l - 1]) * 10

(3)抑梯度異常初始化:參見梯度消失和梯度爆炸的那一個(gè)視頻,參數(shù)名為initialization = “he”铃将,核心代碼:

parameters['W' + str(l)] = np.random.randn(layers_dims[l], layers_dims[l - 1]) * np.sqrt(2 / layers_dims[l - 1])

首先來看我們模型是怎樣的:

def model(X,Y,learning_rate=0.01,num_iterations=15000,print_cost=True,initialization="he",is_polt=True):
    """
    實(shí)現(xiàn)一個(gè)三層的神經(jīng)網(wǎng)絡(luò):LINEAR ->RELU -> LINEAR -> RELU -> LINEAR -> SIGMOID
    
    參數(shù):
        X - 輸入的數(shù)據(jù)项鬼,維度為(2, 要訓(xùn)練/測試的數(shù)量)
        Y - 標(biāo)簽,【0 | 1】劲阎,維度為(1绘盟,對應(yīng)的是輸入的數(shù)據(jù)的標(biāo)簽)
        learning_rate - 學(xué)習(xí)速率
        num_iterations - 迭代的次數(shù)
        print_cost - 是否打印成本值,每迭代1000次打印一次
        initialization - 字符串類型悯仙,初始化的類型【"zeros" | "random" | "he"】
        is_polt - 是否繪制梯度下降的曲線圖
    返回
        parameters - 學(xué)習(xí)后的參數(shù)
    """
    grads = {}
    costs = []
    m = X.shape[1]
    layers_dims = [X.shape[0],10,5,1]
    
    #選擇初始化參數(shù)的類型
    if initialization == "zeros":
        parameters = initialize_parameters_zeros(layers_dims)
    elif initialization == "random":
        parameters = initialize_parameters_random(layers_dims)
    elif initialization == "he":
        parameters = initialize_parameters_he(layers_dims)
    else : 
        print("錯(cuò)誤的初始化參數(shù)龄毡!程序退出")
        exit
    
    #開始學(xué)習(xí)
    for i in range(0,num_iterations):
        #前向傳播
        a3 , cache = init_utils.forward_propagation(X,parameters)
        
        #計(jì)算成本        
        cost = init_utils.compute_loss(a3,Y)
        
        #反向傳播
        grads = init_utils.backward_propagation(X,Y,cache)
        
        #更新參數(shù)
        parameters = init_utils.update_parameters(parameters,grads,learning_rate)
        
        #記錄成本
        if i % 1000 == 0:
            costs.append(cost)
            #打印成本
            if print_cost:
                print("第" + str(i) + "次迭代,成本值為:" + str(cost))
        
    
    #學(xué)習(xí)完畢锡垄,繪制成本曲線
    if is_polt:
        plt.plot(costs)
        plt.ylabel('cost')
        plt.xlabel('iterations (per hundreds)')
        plt.title("Learning rate =" + str(learning_rate))
        plt.show()
    
    #返回學(xué)習(xí)完畢后的參數(shù)
    return parameters

模型看完之后沦零,現(xiàn)在嘗試三種初始化功能:

初始化為零
def initialize_parameters_zeros(layers_dims):
    """
    將模型的參數(shù)全部設(shè)置為0
    
    參數(shù):
        layers_dims - 列表,模型的層數(shù)和對應(yīng)每一層的節(jié)點(diǎn)的數(shù)量
    返回
        parameters - 包含了所有W和b的字典
            W1 - 權(quán)重矩陣货岭,維度為(layers_dims[1], layers_dims[0])
            b1 - 偏置向量路操,維度為(layers_dims[1],1)
            ···
            WL - 權(quán)重矩陣,維度為(layers_dims[L], layers_dims[L -1])
            bL - 偏置向量茴她,維度為(layers_dims[L],1)
    """
    parameters = {}
    
    L = len(layers_dims) #網(wǎng)絡(luò)層數(shù)
    
    for l in range(1,L):
        parameters["W" + str(l)] = np.zeros((layers_dims[l],layers_dims[l-1]))
        parameters["b" + str(l)] = np.zeros((layers_dims[l],1))
        
        #使用斷言確保我的數(shù)據(jù)格式是正確的
        assert(parameters["W" + str(l)].shape == (layers_dims[l],layers_dims[l-1]))
        assert(parameters["b" + str(l)].shape == (layers_dims[l],1))
        
    return parameters

測試一下:

parameters = initialize_parameters_zeros([3,2,1])
print("W1 = " + str(parameters["W1"]))
print("b1 = " + str(parameters["b1"]))
print("W2 = " + str(parameters["W2"]))
print("b2 = " + str(parameters["b2"]))

測試結(jié)果:

W1 = [[0. 0. 0.]
 [0. 0. 0.]]
b1 = [[0.]
 [0.]]
W2 = [[0. 0.]]
b2 = [[0.]]

現(xiàn)在w和b全部初始化為零寻拂,那么我們使用這些參數(shù)來訓(xùn)練模型,試一下結(jié)果怎么樣:

parameters = model(train_X, train_Y, initialization = "zeros",is_polt=True)

測試結(jié)果如下:

第0次迭代丈牢,成本值為:0.6931471805599453
第1000次迭代祭钉,成本值為:0.6931471805599453
第2000次迭代,成本值為:0.6931471805599453
第3000次迭代己沛,成本值為:0.6931471805599453
第4000次迭代慌核,成本值為:0.6931471805599453
第5000次迭代,成本值為:0.6931471805599453
第6000次迭代申尼,成本值為:0.6931471805599453
第7000次迭代垮卓,成本值為:0.6931471805599453
第8000次迭代,成本值為:0.6931471805599453
第9000次迭代师幕,成本值為:0.6931471805599453
第10000次迭代粟按,成本值為:0.6931471805599455
第11000次迭代,成本值為:0.6931471805599453
第12000次迭代霹粥,成本值為:0.6931471805599453
第13000次迭代灭将,成本值為:0.6931471805599453
第14000次迭代,成本值為:0.6931471805599453
誤差函數(shù)

從上圖中我們可以看出學(xué)習(xí)率并沒有變化后控,也就是說這個(gè)模型沒有學(xué)習(xí)任何東西庙曙,現(xiàn)在來看一下預(yù)測結(jié)果:

print ("訓(xùn)練集:")
predictions_train = init_utils.predict(train_X, train_Y, parameters)
print ("測試集:")
predictions_test = init_utils.predict(test_X, test_Y, parameters)

結(jié)果如下:

訓(xùn)練集:
Accuracy: 0.5
測試集:
Accuracy: 0.5

由測試結(jié)果可知,測試性能很差浩淘,并且成本并沒有真正降低捌朴,算法的性能也比隨機(jī)猜測要好一點(diǎn)吴攒,具體細(xì)節(jié)讓我們看下預(yù)測和決策邊界的細(xì)節(jié):

print("predictions_train = " + str(predictions_train))
print("predictions_test = " + str(predictions_test))

plt.title("Model with Zeros initialization")
axes = plt.gca()
axes.set_xlim([-1.5, 1.5])
axes.set_ylim([-1.5, 1.5])
init_utils.plot_decision_boundary(lambda x: init_utils.predict_dec(parameters, x.T), train_X, train_Y)

結(jié)果如下:

predictions_train = [[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
  0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
  0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
  0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
  0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
  0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
  0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
  0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
  0 0 0 0 0 0 0 0 0 0 0 0]]
predictions_test = [[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
  0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
  0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]]
分類結(jié)果

分類失敗,該模型預(yù)測每個(gè)都是0砂蔽,通常來說洼怔,零初始化會導(dǎo)致神經(jīng)網(wǎng)絡(luò)無法打破對稱性,最終導(dǎo)致的結(jié)果就是無論網(wǎng)絡(luò)有多少層左驾,最終只能得到和logistic函數(shù)相同的結(jié)果茴厉。

隨機(jī)初始化

為了打破對稱性,我們可以把參數(shù)隨機(jī)賦值什荣,在隨機(jī)初始化之后,每個(gè)神經(jīng)元可以開始學(xué)習(xí)其輸入的不同功能怀酷。

def initialize_parameters_random(layers_dims):
    """
    參數(shù):
        layers_dims - 列表稻爬,模型的層數(shù)和對應(yīng)每一層的節(jié)點(diǎn)的數(shù)量
    返回
        parameters - 包含了所有W和b的字典
            W1 - 權(quán)重矩陣,維度為(layers_dims[1], layers_dims[0])
            b1 - 偏置向量蜕依,維度為(layers_dims[1],1)
            ···
            WL - 權(quán)重矩陣桅锄,維度為(layers_dims[L], layers_dims[L -1])
            b1 - 偏置向量,維度為(layers_dims[L],1)
    """
    
    np.random.seed(3)               # 指定隨機(jī)種子
    parameters = {}
    L = len(layers_dims)            # 層數(shù)
    
    for l in range(1, L):
        parameters['W' + str(l)] = np.random.randn(layers_dims[l], layers_dims[l - 1]) * 10 #使用10倍縮放
        parameters['b' + str(l)] = np.zeros((layers_dims[l], 1))
        
        #使用斷言確保我的數(shù)據(jù)格式是正確的
        assert(parameters["W" + str(l)].shape == (layers_dims[l],layers_dims[l-1]))
        assert(parameters["b" + str(l)].shape == (layers_dims[l],1))
        
    return parameters

然后測試一下:

parameters = initialize_parameters_random([3, 2, 1])
print("W1 = " + str(parameters["W1"]))
print("b1 = " + str(parameters["b1"]))
print("W2 = " + str(parameters["W2"]))
print("b2 = " + str(parameters["b2"]))

測試結(jié)果:

W1 = [[ 17.88628473   4.36509851   0.96497468]
 [-18.63492703  -2.77388203  -3.54758979]]
b1 = [[0.]
 [0.]]
W2 = [[-0.82741481 -6.27000677]]
b2 = [[0.]]

然后訓(xùn)練預(yù)測試:

parameters = model(train_X, train_Y, initialization = "random",is_polt=True)
print("訓(xùn)練集:")
predictions_train = init_utils.predict(train_X, train_Y, parameters)
print("測試集:")
predictions_test = init_utils.predict(test_X, test_Y, parameters)

print(predictions_train)
print(predictions_test)

預(yù)測結(jié)果:

訓(xùn)練集:
Accuracy: 0.83
測試集:
Accuracy: 0.86
[[1 0 1 1 0 0 1 1 1 1 1 0 1 0 0 1 0 1 1 0 0 0 1 0 1 1 1 1 1 1 0 1 1 0 0 1
  1 1 1 1 1 1 1 0 1 1 1 1 0 1 0 1 1 1 1 0 0 1 1 1 1 0 1 1 0 1 0 1 1 1 1 0
  0 0 0 0 1 0 1 0 1 1 1 0 0 1 1 1 1 1 1 0 0 1 1 1 0 1 1 0 1 0 1 1 0 1 1 0
  1 0 1 1 0 0 1 0 0 1 1 0 1 1 1 0 1 0 0 1 0 1 1 1 1 1 1 1 0 1 1 0 0 1 1 0
  0 0 1 0 1 0 1 0 1 1 1 0 0 1 1 1 1 0 1 1 0 1 0 1 1 0 1 0 1 1 1 1 0 1 1 1
  1 0 1 0 1 0 1 1 1 1 0 1 1 0 1 1 0 1 1 0 1 0 1 1 1 0 1 1 1 0 1 0 1 0 0 1
  0 1 1 0 1 1 0 1 1 0 1 1 1 0 1 1 1 1 0 1 0 0 1 1 0 1 1 1 0 0 0 1 1 0 1 1
  1 1 0 1 1 0 1 1 1 0 0 1 0 0 0 1 0 0 0 1 1 1 1 0 0 0 0 1 1 1 1 0 0 1 1 1
  1 1 1 1 0 0 0 1 1 1 1 0]]
[[1 1 1 1 0 1 0 1 1 0 1 1 1 0 0 0 0 1 0 1 0 0 1 0 1 0 1 1 1 1 1 0 0 0 0 1
  0 1 1 0 0 1 1 1 1 1 0 1 1 1 0 1 0 1 1 0 1 0 1 0 1 1 1 1 1 1 1 1 1 0 1 0
  1 1 1 1 1 0 1 0 0 1 0 0 0 1 1 0 1 1 0 0 0 1 1 0 1 1 0 0]]

然后我們將圖繪制出來:

plt.title("Model with large random initialization")
axes = plt.gca()
axes.set_xlim([-1.5, 1.5])
axes.set_ylim([-1.5, 1.5])
init_utils.plot_decision_boundary(lambda x: init_utils.predict_dec(parameters, x.T), train_X, train_Y)
繪制結(jié)果
誤差函數(shù)

從上圖我們可以看到誤差開始很高样眠。這是由于具有較大的隨機(jī)權(quán)重友瘤,最終的激活(sigmoid)輸出的結(jié)果非常接近于0或者1,而當(dāng)它出現(xiàn)錯(cuò)誤時(shí)檐束,他會導(dǎo)致非常高的損失辫秧。初始化參數(shù)如果沒有很好的話會導(dǎo)致梯度消失、爆炸被丧,這也會減慢優(yōu)化算法盟戏。如果我們對這個(gè)網(wǎng)絡(luò)進(jìn)行更長時(shí)間的訓(xùn)練,我們將看到更好的結(jié)果甥桂,但是使用過大的隨機(jī)數(shù)初始化會減慢優(yōu)化的速度柿究。
所以在將權(quán)重初始化的時(shí)候,初始化的值非常大的效果并不好黄选,下面我們來試一下小一點(diǎn)的參數(shù)蝇摸。

抑梯度異常初始化

我們初始化參數(shù)的時(shí)候使用公式如下:


抑梯度異常公式
def initialize_parameters_he(layers_dims):
    """
    參數(shù):
        layers_dims - 列表,模型的層數(shù)和對應(yīng)每一層的節(jié)點(diǎn)的數(shù)量
    返回
        parameters - 包含了所有W和b的字典
            W1 - 權(quán)重矩陣办陷,維度為(layers_dims[1], layers_dims[0])
            b1 - 偏置向量貌夕,維度為(layers_dims[1],1)
            ···
            WL - 權(quán)重矩陣,維度為(layers_dims[L], layers_dims[L -1])
            b1 - 偏置向量懂诗,維度為(layers_dims[L],1)
    """
    
    np.random.seed(3)               # 指定隨機(jī)種子
    parameters = {}
    L = len(layers_dims)            # 層數(shù)
    
    for l in range(1, L):
        parameters['W' + str(l)] = np.random.randn(layers_dims[l], layers_dims[l - 1]) * np.sqrt(2 / layers_dims[l - 1])
        parameters['b' + str(l)] = np.zeros((layers_dims[l], 1))
        
        #使用斷言確保我的數(shù)據(jù)格式是正確的
        assert(parameters["W" + str(l)].shape == (layers_dims[l],layers_dims[l-1]))
        assert(parameters["b" + str(l)].shape == (layers_dims[l],1))
        
    return parameters

測試代碼如下:

parameters = initialize_parameters_he([2, 4, 1])
print("W1 = " + str(parameters["W1"]))
print("b1 = " + str(parameters["b1"]))
print("W2 = " + str(parameters["W2"]))
print("b2 = " + str(parameters["b2"]))
parameters = model(train_X, train_Y, initialization = "he",is_polt=True)
print("訓(xùn)練集:")
predictions_train = init_utils.predict(train_X, train_Y, parameters)
print("測試集:")
init_utils.predictions_test = init_utils.predict(test_X, test_Y, parameters)

測試結(jié)果如下:

W1 = [[ 1.78862847  0.43650985]
 [ 0.09649747 -1.8634927 ]
 [-0.2773882  -0.35475898]
 [-0.08274148 -0.62700068]]
b1 = [[0.]
 [0.]
 [0.]
 [0.]]
W2 = [[-0.03098412 -0.33744411 -0.92904268  0.62552248]]
b2 = [[0.]]
第0次迭代蜂嗽,成本值為:0.8830537463419761
第1000次迭代,成本值為:0.6879825919728063
第2000次迭代殃恒,成本值為:0.6751286264523371
第3000次迭代植旧,成本值為:0.6526117768893807
第4000次迭代辱揭,成本值為:0.6082958970572938
第5000次迭代,成本值為:0.5304944491717495
第6000次迭代病附,成本值為:0.4138645817071795
第7000次迭代问窃,成本值為:0.31178034648444414
第8000次迭代,成本值為:0.2369621533032257
第9000次迭代完沪,成本值為:0.18597287209206845
第10000次迭代域庇,成本值為:0.1501555628037181
第11000次迭代,成本值為:0.12325079292273548
第12000次迭代覆积,成本值為:0.09917746546525937
第13000次迭代听皿,成本值為:0.08457055954024273
第14000次迭代,成本值為:0.07357895962677366
訓(xùn)練集:
Accuracy: 0.9933333333333333
測試集:
Accuracy: 0.96

通過上述的公式帶入宽档,初始值都變得很小尉姨,通過訓(xùn)練,準(zhǔn)確率有了明顯的提高吗冤。接下來繪制一下預(yù)測情況:

plt.title("Model with He initialization")
axes = plt.gca()
axes.set_xlim([-1.5, 1.5])
axes.set_ylim([-1.5, 1.5])
init_utils.plot_decision_boundary(lambda x: init_utils.predict_dec(parameters, x.T), train_X, train_Y)
模型訓(xùn)練結(jié)果

由上圖可知又厉,該模型訓(xùn)練的結(jié)果已經(jīng)變得非常不錯(cuò)了。
初始化的模型將藍(lán)色和紅色的點(diǎn)在少量的迭代中很好地分離出來椎瘟,總結(jié)一下:
1.不同的初始化方法可能導(dǎo)致性能最終不同
2.隨機(jī)初始化有助于打破對稱覆致,使得不同隱藏層的單元可以學(xué)習(xí)到不同的參數(shù)。
3.初始化時(shí)肺蔚,初始值不宜過大煌妈。
4.He初始化搭配ReLU激活函數(shù)常常可以得到不錯(cuò)的效果宣羊。

正則化模型

在深度學(xué)習(xí)中声旺,如果數(shù)據(jù)集沒有足夠大的話,可能導(dǎo)致一些過擬合的問題段只。過擬合就是在訓(xùn)練集上精確度很高腮猖,但是在測試集上精確度卻不高。正則化模型則惡能夠有效的避免過擬合赞枕。

Problem Statement: You have just been hired as an AI expert by the French Football Corporation. They would like you to recommend positions where France’s goal keeper should kick the ball so that the French team’s players can then hit it with their head.

讀取并繪制數(shù)據(jù)集

我們加載并查看一下我們的數(shù)據(jù)集:

train_X, train_Y, test_X, test_Y = reg_utils.load_2D_dataset(is_plot=True)

查看結(jié)果:

數(shù)據(jù)集模型

每一個(gè)點(diǎn)代表球落下的可能的位置澈缺,藍(lán)色代表已方球員會搶到球,紅色代表對手的球員會搶到球炕婶,我們要做的就是使用模型來畫一條線姐赡,來找到適合我方球員能搶到球的位置。
我們要做以下三件事柠掂,來對比出不同的模型的優(yōu)劣:
1.不使用正則化
2.使用正則化
(1)使用L2正則化
(2)使用隨機(jī)節(jié)點(diǎn)刪除
我們來看下模型:
正則化模式:將lambd輸入設(shè)置為非零值
隨機(jī)刪除節(jié)點(diǎn):將keep_prob設(shè)置為小于1的值

def  model(X,Y,learning_rate=0.3,num_iterations=30000,print_cost=True,is_plot=True,lambd=0,keep_prob=1):
    """
    實(shí)現(xiàn)一個(gè)三層的神經(jīng)網(wǎng)絡(luò):LINEAR ->RELU -> LINEAR -> RELU -> LINEAR -> SIGMOID
    
    參數(shù):
        X - 輸入的數(shù)據(jù)项滑,維度為(2, 要訓(xùn)練/測試的數(shù)量)
        Y - 標(biāo)簽,【0(藍(lán)色) | 1(紅色)】涯贞,維度為(1枪狂,對應(yīng)的是輸入的數(shù)據(jù)的標(biāo)簽)
        learning_rate - 學(xué)習(xí)速率
        num_iterations - 迭代的次數(shù)
        print_cost - 是否打印成本值危喉,每迭代10000次打印一次,但是每1000次記錄一個(gè)成本值
        is_polt - 是否繪制梯度下降的曲線圖
        lambd - 正則化的超參數(shù)州疾,實(shí)數(shù)
        keep_prob - 隨機(jī)刪除節(jié)點(diǎn)的概率
    返回
        parameters - 學(xué)習(xí)后的參數(shù)
    """
    grads = {}
    costs = []
    m = X.shape[1]
    layers_dims = [X.shape[0],20,3,1]
    
    #初始化參數(shù)
    parameters = reg_utils.initialize_parameters(layers_dims)
    
    #開始學(xué)習(xí)
    for i in range(0,num_iterations):
        #前向傳播
        ##是否隨機(jī)刪除節(jié)點(diǎn)
        if keep_prob == 1:
            ###不隨機(jī)刪除節(jié)點(diǎn)
            a3 , cache = reg_utils.forward_propagation(X,parameters)
        elif keep_prob < 1:
            ###隨機(jī)刪除節(jié)點(diǎn)
            a3 , cache = forward_propagation_with_dropout(X,parameters,keep_prob)
        else:
            print("keep_prob參數(shù)錯(cuò)誤辜限!程序退出。")
            exit
        
        #計(jì)算成本
        ## 是否使用二范數(shù)
        if lambd == 0:
            ###不使用L2正則化
            cost = reg_utils.compute_cost(a3,Y)
        else:
            ###使用L2正則化
            cost = compute_cost_with_regularization(a3,Y,parameters,lambd)
        
        #反向傳播
        ##可以同時(shí)使用L2正則化和隨機(jī)刪除節(jié)點(diǎn)严蓖,但是本次實(shí)驗(yàn)不同時(shí)使用薄嫡。
        assert(lambd == 0  or keep_prob ==1)
        
        ##兩個(gè)參數(shù)的使用情況
        if (lambd == 0 and keep_prob == 1):
            ### 不使用L2正則化和不使用隨機(jī)刪除節(jié)點(diǎn)
            grads = reg_utils.backward_propagation(X,Y,cache)
        elif lambd != 0:
            ### 使用L2正則化,不使用隨機(jī)刪除節(jié)點(diǎn)
            grads = backward_propagation_with_regularization(X, Y, cache, lambd)
        elif keep_prob < 1:
            ### 使用隨機(jī)刪除節(jié)點(diǎn)颗胡,不使用L2正則化
            grads = backward_propagation_with_dropout(X, Y, cache, keep_prob)
        
        #更新參數(shù)
        parameters = reg_utils.update_parameters(parameters, grads, learning_rate)
        
        #記錄并打印成本
        if i % 1000 == 0:
            ## 記錄成本
            costs.append(cost)
            if (print_cost and i % 10000 == 0):
                #打印成本
                print("第" + str(i) + "次迭代毫深,成本值為:" + str(cost))
        
    #是否繪制成本曲線圖
    if is_plot:
        plt.plot(costs)
        plt.ylabel('cost')
        plt.xlabel('iterations (x1,000)')
        plt.title("Learning rate =" + str(learning_rate))
        plt.show()
    
    #返回學(xué)習(xí)后的參數(shù)
    return parameters

首先我們看下不使用正則化下的模型效果:

parameters = model(train_X, train_Y,is_plot=True)
print("訓(xùn)練集:")
predictions_train = reg_utils.predict(train_X, train_Y, parameters)
print("測試集:")
predictions_test = reg_utils.predict(test_X, test_Y, parameters)

模型結(jié)果:

第0次迭代,成本值為:0.6557412523481002
第10000次迭代毒姨,成本值為:0.16329987525724218
第20000次迭代费什,成本值為:0.13851642423264765
訓(xùn)練集:
Accuracy: 0.9478672985781991
測試集:
Accuracy: 0.915
成本曲線圖

我們可以看出,對于訓(xùn)練集手素,精確度為94.7%;但是對于測試集瘩蚪,精確度卻才91.5%泉懦。接下來,我們將模型分割曲線畫出來:

![模型效果圖](https://upload-images.jianshu.io/upload_images/15145168-7cf7f3ee0ef85df1.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
從圖中可以看出疹瘦,在沒有正則化的時(shí)候崩哩,分割曲線有明顯的過擬合特性。接下來言沐,我們使用L2正則化:
#####使用正則化
L2正則化
避免過度擬合的標(biāo)準(zhǔn)方法稱為L2正則化邓嘹,它包括適當(dāng)修改你的成本函數(shù),如下公式所示:
![L2正則化公式](https://upload-images.jianshu.io/upload_images/15145168-e256227f917185a9.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
需要注意的是在前向傳播中對W的操作险胰,要將這三層網(wǎng)絡(luò)的W相加并乘以(1/m+lambd/2)汹押。在后向傳播中,使用

def compute_cost_with_regularization(A3,Y,parameters,lambd):
"""
實(shí)現(xiàn)公式2的L2正則化計(jì)算成本

參數(shù):
    A3 - 正向傳播的輸出結(jié)果起便,維度為(輸出節(jié)點(diǎn)數(shù)量棚贾,訓(xùn)練/測試的數(shù)量)
    Y - 標(biāo)簽向量,與數(shù)據(jù)一一對應(yīng)榆综,維度為(輸出節(jié)點(diǎn)數(shù)量妙痹,訓(xùn)練/測試的數(shù)量)
    parameters - 包含模型學(xué)習(xí)后的參數(shù)的字典
返回:
    cost - 使用公式2計(jì)算出來的正則化損失的值

"""
m = Y.shape[1]
W1 = parameters["W1"]
W2 = parameters["W2"]
W3 = parameters["W3"]

cross_entropy_cost = reg_utils.compute_cost(A3,Y)

L2_regularization_cost = lambd * (np.sum(np.square(W1)) + np.sum(np.square(W2))  + np.sum(np.square(W3))) / (2 * m)

cost = cross_entropy_cost + L2_regularization_cost

return cost

當(dāng)然,因?yàn)楦淖兞顺杀竞瘮?shù)鼻疮,我們也必須改變向后傳播的函數(shù)怯伊, 所有的梯度都必須根據(jù)這個(gè)新的成本值來計(jì)算。

def backward_propagation_with_regularization(X, Y, cache, lambd):
    """
    實(shí)現(xiàn)我們添加了L2正則化的模型的后向傳播判沟。
    
    參數(shù):
        X - 輸入數(shù)據(jù)集耿芹,維度為(輸入節(jié)點(diǎn)數(shù)量崭篡,數(shù)據(jù)集里面的數(shù)量)
        Y - 標(biāo)簽,維度為(輸出節(jié)點(diǎn)數(shù)量猩系,數(shù)據(jù)集里面的數(shù)量)
        cache - 來自forward_propagation()的cache輸出
        lambda - regularization超參數(shù)媚送,實(shí)數(shù)
    
    返回:
        gradients - 一個(gè)包含了每個(gè)參數(shù)、激活值和預(yù)激活值變量的梯度的字典
    """
    
    m = X.shape[1]
    
    (Z1, A1, W1, b1, Z2, A2, W2, b2, Z3, A3, W3, b3) = cache
    
    dZ3 = A3 - Y
    
    dW3 = (1 / m) * np.dot(dZ3,A2.T) + ((lambd * W3) / m )
    db3 = (1 / m) * np.sum(dZ3,axis=1,keepdims=True)
    
    dA2 = np.dot(W3.T,dZ3)
    dZ2 = np.multiply(dA2,np.int64(A2 > 0))
    dW2 = (1 / m) * np.dot(dZ2,A1.T) + ((lambd * W2) / m)
    db2 = (1 / m) * np.sum(dZ2,axis=1,keepdims=True)
    
    dA1 = np.dot(W2.T,dZ2)
    dZ1 = np.multiply(dA1,np.int64(A1 > 0))
    dW1 = (1 / m) * np.dot(dZ1,X.T) + ((lambd * W1) / m)
    db1 = (1 / m) * np.sum(dZ1,axis=1,keepdims=True)
    
    gradients = {"dZ3": dZ3, "dW3": dW3, "db3": db3, "dA2": dA2,
                 "dZ2": dZ2, "dW2": dW2, "db2": db2, "dA1": dA1, 
                 "dZ1": dZ1, "dW1": dW1, "db1": db1}
    
    return gradients

測試一下:

parameters = model(train_X, train_Y, lambd=0.7,is_plot=True)
print("使用正則化寇甸,訓(xùn)練集:")
predictions_train = reg_utils.predict(train_X, train_Y, parameters)
print("使用正則化塘偎,測試集:")
predictions_test = reg_utils.predict(test_X, test_Y, parameters)

執(zhí)行結(jié)果:

第0次迭代,成本值為:0.6974484493131264
第10000次迭代拿霉,成本值為:0.2684918873282239
第20000次迭代吟秩,成本值為:0.2680916337127301
使用正則化,訓(xùn)練集:
Accuracy: 0.9383886255924171
使用正則化绽淘,測試集:
Accuracy: 0.93
損失函數(shù)plt.title("Model with L2-regularization")

接下來繪制一下模型結(jié)果

plt.title("Model with L2-regularization")
axes = plt.gca()
axes.set_xlim([-0.75,0.40])
axes.set_ylim([-0.75,0.65])
reg_utils.plot_decision_boundary(lambda x: reg_utils.predict_dec(parameters, x.T), train_X, train_Y)

繪制結(jié)果:


模型結(jié)果

lanmd的值是可以使用開發(fā)集調(diào)整時(shí)的超參數(shù)涵防。L2正則化會使決策邊界更加平滑。如果lanmd太大沪铭,也可能過度平滑壮池,從而導(dǎo)致模型高偏差。L2正則化依賴于較小權(quán)重的模型比具有較大權(quán)重的mooing更簡單這樣的假設(shè)杀怠,因此通過消減成本函數(shù)中權(quán)重的平房值椰憋,可以將權(quán)重值逐漸改變到較小的值。權(quán)值數(shù)高的話會有更平滑的模型赔退,其中輸入變化時(shí)輸出變化更慢橙依,但是需要花費(fèi)更多的時(shí)間。L2的主要影響如下:
成本函數(shù):正則化的計(jì)算需要添加到成本函數(shù)中
反向傳播功能:在權(quán)重矩陣方面硕旗,梯度計(jì)算時(shí)也要依據(jù)正則化來做相應(yīng)的計(jì)算
重量變小:權(quán)重被逐漸改變到較小的值

隨機(jī)刪除節(jié)點(diǎn)

最后窗骑,我們使用dropout來進(jìn)行正則化,dropout的原理就是每次迭代過程中隨機(jī)將其中的一些點(diǎn)失效漆枚。當(dāng)我們關(guān)閉一些節(jié)點(diǎn)時(shí)创译,我們實(shí)際上修改了我們的模型。背后的想法是墙基,在每次迭代時(shí)昔榴,我們都會訓(xùn)練一個(gè)只使用一部分神經(jīng)元的不同模型。隨著迭代次數(shù)的增加碘橘,我們的模型節(jié)點(diǎn)會對其他特定節(jié)點(diǎn)的激活變得不那么敏感互订,因?yàn)槠渌?jié)點(diǎn)可能在任何時(shí)候會失效。


第二層隨機(jī)節(jié)點(diǎn)刪除

在每一次迭代中痘拆,關(guān)閉一層的每個(gè)神經(jīng)元仰禽,概率為1-keep_brob,我們在這兒保持概率為keep_prob。丟棄的節(jié)點(diǎn)都不參與迭代時(shí)的前向后向傳播吐葵。


第一層和第三層啟用隨機(jī)刪除

如上圖所示规揪,我們要關(guān)閉上圖中第一層和第三層的一些節(jié)點(diǎn),現(xiàn)在我們需要做以下四步:
1.在視頻中温峭,吳恩達(dá)老師講解了使用np.random.rand() 來初始化和a[1]具有相同維度的 d[1]猛铅,在這里,我們將使用向量化實(shí)現(xiàn)凤藏,我們先來實(shí)現(xiàn)一個(gè)和A[1]相同的隨機(jī)矩陣D[1]

2.如果D[1]低于(keep_prob)的值我們就把它設(shè)置為0奸忽,如果高于(keep_prob)的值就設(shè)置為1
3.把A[1]更新為A[1]*D[1]。我們可以使用D[1]作為掩碼揖庄。我們做矩陣相乘的時(shí)候栗菜,關(guān)閉的那些節(jié)點(diǎn)(值為0)就會不參與計(jì)算,因?yàn)?乘以任何數(shù)都是0蹄梢。
4.使用A[1]除以keep_prob疙筹。這樣做的話我們通過縮放就在計(jì)算成本的時(shí)候仍然具有相同的期望值,這叫做反向dropout禁炒。

def forward_propagation_with_dropout(X,parameters,keep_prob=0.5):
    """
    實(shí)現(xiàn)具有隨機(jī)舍棄節(jié)點(diǎn)的前向傳播而咆。
    LINEAR -> RELU + DROPOUT -> LINEAR -> RELU + DROPOUT -> LINEAR -> SIGMOID.
    
    參數(shù):
        X  - 輸入數(shù)據(jù)集,維度為(2幕袱,示例數(shù))
        parameters - 包含參數(shù)“W1”暴备,“b1”,“W2”凹蜂,“b2”,“W3”阁危,“b3”的python字典:
            W1  - 權(quán)重矩陣玛痊,維度為(20,2)
            b1  - 偏向量,維度為(20,1)
            W2  - 權(quán)重矩陣狂打,維度為(3,20)
            b2  - 偏向量擂煞,維度為(3,1)
            W3  - 權(quán)重矩陣,維度為(1,3)
            b3  - 偏向量趴乡,維度為(1,1)
        keep_prob  - 隨機(jī)刪除的概率对省,實(shí)數(shù)
    返回:
        A3  - 最后的激活值,維度為(1,1)晾捏,正向傳播的輸出
        cache - 存儲了一些用于計(jì)算反向傳播的數(shù)值的元組
    """
    np.random.seed(1)
    
    W1 = parameters["W1"]
    b1 = parameters["b1"]
    W2 = parameters["W2"]
    b2 = parameters["b2"]
    W3 = parameters["W3"]
    b3 = parameters["b3"]
    
    #LINEAR -> RELU -> LINEAR -> RELU -> LINEAR -> SIGMOID
    Z1 = np.dot(W1,X) + b1
    A1 = reg_utils.relu(Z1)
    
    #下面的步驟1-4對應(yīng)于上述的步驟1-4。
    D1 = np.random.rand(A1.shape[0],A1.shape[1])    #步驟1:初始化矩陣D1 = np.random.rand(..., ...)
    D1 = D1 < keep_prob                             #步驟2:將D1的值轉(zhuǎn)換為0或1(使??用keep_prob作為閾值)
    A1 = A1 * D1                                    #步驟3:舍棄A1的一些節(jié)點(diǎn)(將它的值變?yōu)?或False)
    A1 = A1 / keep_prob                             #步驟4:縮放未舍棄的節(jié)點(diǎn)(不為0)的值
    """
    #不理解的同學(xué)運(yùn)行一下下面代碼就知道了。
    import numpy as np
    np.random.seed(1)
    A1 = np.random.randn(1,3)
    
    D1 = np.random.rand(A1.shape[0],A1.shape[1])
    keep_prob=0.5
    D1 = D1 < keep_prob
    print(D1)
    
    A1 = 0.01
    A1 = A1 * D1
    A1 = A1 / keep_prob
    print(A1)
    """
    
    Z2 = np.dot(W2,A1) + b2
    A2 = reg_utils.relu(Z2)
    
    #下面的步驟1-4對應(yīng)于上述的步驟1-4上真。
    D2 = np.random.rand(A2.shape[0],A2.shape[1])    #步驟1:初始化矩陣D2 = np.random.rand(..., ...)
    D2 = D2 < keep_prob                             #步驟2:將D2的值轉(zhuǎn)換為0或1(使??用keep_prob作為閾值)
    A2 = A2 * D2                                    #步驟3:舍棄A1的一些節(jié)點(diǎn)(將它的值變?yōu)?或False)
    A2 = A2 / keep_prob                             #步驟4:縮放未舍棄的節(jié)點(diǎn)(不為0)的值
    
    Z3 = np.dot(W3, A2) + b3
    A3 = reg_utils.sigmoid(Z3)
    
    cache = (Z1, D1, A1, W1, b1, Z2, D2, A2, W2, b2, Z3, A3, W3, b3)
    
    return A3, cache

改變了前向傳播算法悦穿,我們也需要改變后向傳播的算法,使用存儲在緩存中的掩碼D[1]和D[2]將舍棄的節(jié)點(diǎn)位置信息添加到第一個(gè)和第二個(gè)隱藏層。

def backward_propagation_with_dropout(X,Y,cache,keep_prob):
    """
    實(shí)現(xiàn)我們隨機(jī)刪除的模型的后向傳播玻淑。
    參數(shù):
        X  - 輸入數(shù)據(jù)集嗽冒,維度為(2,示例數(shù))
        Y  - 標(biāo)簽补履,維度為(輸出節(jié)點(diǎn)數(shù)量添坊,示例數(shù)量)
        cache - 來自forward_propagation_with_dropout()的cache輸出
        keep_prob  - 隨機(jī)刪除的概率,實(shí)數(shù)
    
    返回:
        gradients - 一個(gè)關(guān)于每個(gè)參數(shù)箫锤、激活值和預(yù)激活變量的梯度值的字典
    """
    m = X.shape[1]
    (Z1, D1, A1, W1, b1, Z2, D2, A2, W2, b2, Z3, A3, W3, b3) = cache
    
    dZ3 = A3 - Y
    dW3 = (1 / m) * np.dot(dZ3,A2.T)
    db3 = 1. / m * np.sum(dZ3, axis=1, keepdims=True)
    dA2 = np.dot(W3.T, dZ3)
    
    dA2 = dA2 * D2          # 步驟1:使用正向傳播期間相同的節(jié)點(diǎn)贬蛙,舍棄那些關(guān)閉的節(jié)點(diǎn)(因?yàn)槿魏螖?shù)乘以0或者False都為0或者False)
    dA2 = dA2 / keep_prob   # 步驟2:縮放未舍棄的節(jié)點(diǎn)(不為0)的值
    
    dZ2 = np.multiply(dA2, np.int64(A2 > 0))
    dW2 = 1. / m * np.dot(dZ2, A1.T)
    db2 = 1. / m * np.sum(dZ2, axis=1, keepdims=True)
    
    dA1 = np.dot(W2.T, dZ2)
    
    dA1 = dA1 * D1          # 步驟1:使用正向傳播期間相同的節(jié)點(diǎn),舍棄那些關(guān)閉的節(jié)點(diǎn)(因?yàn)槿魏螖?shù)乘以0或者False都為0或者False)
    dA1 = dA1 / keep_prob   # 步驟2:縮放未舍棄的節(jié)點(diǎn)(不為0)的值

    dZ1 = np.multiply(dA1, np.int64(A1 > 0))
    dW1 = 1. / m * np.dot(dZ1, X.T)
    db1 = 1. / m * np.sum(dZ1, axis=1, keepdims=True)
    
    gradients = {"dZ3": dZ3, "dW3": dW3, "db3": db3,"dA2": dA2,
                 "dZ2": dZ2, "dW2": dW2, "db2": db2, "dA1": dA1, 
                 "dZ1": dZ1, "dW1": dW1, "db1": db1}
    
    return gradients

現(xiàn)在前向后向傳播函數(shù)都寫好了麻汰,現(xiàn)在用dropout運(yùn)行模型(keep_prob=0.86)訓(xùn)練一下速客。這意味著每次迭代中,程序都可以14%的概率關(guān)閉第一層和第二層的神經(jīng)元五鲫。
現(xiàn)在我們測試一下:

parameters = model(train_X, train_Y, keep_prob=0.86, learning_rate=0.3,is_plot=True)

print("使用隨機(jī)刪除節(jié)點(diǎn)溺职,訓(xùn)練集:")
predictions_train = reg_utils.predict(train_X, train_Y, parameters)
print("使用隨機(jī)刪除節(jié)點(diǎn),測試集:")
reg_utils.predictions_test = reg_utils.predict(test_X, test_Y, parameters)

測試結(jié)果:

第0次迭代位喂,成本值為:0.6543912405149825
第10000次迭代浪耘,成本值為:0.061016986574905605
第20000次迭代,成本值為:0.060582435798513114
使用隨機(jī)刪除節(jié)點(diǎn)塑崖,訓(xùn)練集:
Accuracy: 0.9289099526066351
使用隨機(jī)刪除節(jié)點(diǎn)七冲,測試集:
Accuracy: 0.95

下面繪制圖片試一下:

plt.title("Model with dropout")
axes = plt.gca()
axes.set_xlim([-0.75, 0.40])
axes.set_ylim([-0.75, 0.65])
reg_utils.plot_decision_boundary(lambda x: reg_utils.predict_dec(parameters, x.T), train_X, train_Y)

運(yùn)行結(jié)果:


模型效果

我們可以看到,正則化會把訓(xùn)練集的準(zhǔn)確度降低规婆,但是測試集的準(zhǔn)確度提高了澜躺,所以,這個(gè)還是比較成功了抒蚜!


梯度校驗(yàn)下次再寫>虮伞!嗡髓!




完整代碼地址




了解更多請關(guān)注作者微信公眾號:

一技破萬法
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末操漠,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子饿这,更是在濱河造成了極大的恐慌浊伙,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,639評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件长捧,死亡現(xiàn)場離奇詭異嚣鄙,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)串结,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,277評論 3 385
  • 文/潘曉璐 我一進(jìn)店門拗慨,熙熙樓的掌柜王于貴愁眉苦臉地迎上來廓八,“玉大人,你說我怎么就攤上這事赵抢【珲澹” “怎么了?”我有些...
    開封第一講書人閱讀 157,221評論 0 348
  • 文/不壞的土叔 我叫張陵烦却,是天一觀的道長宠叼。 經(jīng)常有香客問我,道長其爵,這世上最難降的妖魔是什么冒冬? 我笑而不...
    開封第一講書人閱讀 56,474評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮摩渺,結(jié)果婚禮上简烤,老公的妹妹穿的比我還像新娘。我一直安慰自己摇幻,他們只是感情好横侦,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,570評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著绰姻,像睡著了一般枉侧。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上狂芋,一...
    開封第一講書人閱讀 49,816評論 1 290
  • 那天榨馁,我揣著相機(jī)與錄音,去河邊找鬼帜矾。 笑死翼虫,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的屡萤。 我是一名探鬼主播珍剑,決...
    沈念sama閱讀 38,957評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼灭衷!你這毒婦竟也來了次慢?” 一聲冷哼從身側(cè)響起旁涤,我...
    開封第一講書人閱讀 37,718評論 0 266
  • 序言:老撾萬榮一對情侶失蹤翔曲,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后劈愚,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體瞳遍,經(jīng)...
    沈念sama閱讀 44,176評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,511評論 2 327
  • 正文 我和宋清朗相戀三年菌羽,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了掠械。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,646評論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖猾蒂,靈堂內(nèi)的尸體忽然破棺而出均唉,到底是詐尸還是另有隱情,我是刑警寧澤肚菠,帶...
    沈念sama閱讀 34,322評論 4 330
  • 正文 年R本政府宣布舔箭,位于F島的核電站,受9級特大地震影響蚊逢,放射性物質(zhì)發(fā)生泄漏层扶。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,934評論 3 313
  • 文/蒙蒙 一烙荷、第九天 我趴在偏房一處隱蔽的房頂上張望镜会。 院中可真熱鬧,春花似錦终抽、人聲如沸戳表。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,755評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽扒袖。三九已至,卻和暖如春亩码,著一層夾襖步出監(jiān)牢的瞬間季率,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,987評論 1 266
  • 我被黑心中介騙來泰國打工描沟, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留飒泻,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,358評論 2 360
  • 正文 我出身青樓吏廉,卻偏偏與公主長得像泞遗,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子席覆,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,514評論 2 348

推薦閱讀更多精彩內(nèi)容