日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 综合教程 >内容正文

综合教程

tensorflow学习笔记——使用TensorFlow操作MNIST数据(1)

發布時間:2024/6/21 综合教程 30 生活家
生活随笔 收集整理的這篇文章主要介紹了 tensorflow学习笔记——使用TensorFlow操作MNIST数据(1) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

  續集請點擊我:tensorflow學習筆記——使用TensorFlow操作MNIST數據(2)

  本節開始學習使用tensorflow教程,當然從最簡單的MNIST開始。這怎么說呢,就好比編程入門有Hello World,機器學習入門有MNIST。在此節,我將訓練一個機器學習模型用于預測圖片里面的數字。

  開始先普及一下基礎知識,我們所說的圖片是通過像素來定義的,即每個像素點的顏色不同,其對應的顏色值不同,例如黑白圖片的顏色值為0到255,手寫體字符,白色的地方為0,黑色為1,如下圖。

  MNIST 是一個非常有名的手寫體數字識別數據集,在很多資料中,這個數據集都會被用做深度學習的入門樣例。而Tensorflow的封裝讓MNIST數據集變得更加方便。MNIST是NIST數據集的一個子集,它包含了60000張圖片作為訓練數據,10000張圖片作為測試數據。在MNIST數據集中的每一張圖片都代表了0~9中的一個數字。圖片的大小都為28*28,且數字都會出現在圖片的正中間,如下圖:

  在上圖中右側顯示了一張數字1的圖片,而右側顯示了這個圖片所對應的像素矩陣,MNIST數據集提供了四個下載文件,在tensorflow中可以將這四個文件直接下載放到一個目錄中并加載,如下代碼input_data.read_data_sets所示,如果指定目錄中沒有數據,那么tensorflow會自動去網絡上進行下載。下面代碼介紹了如何使用tensorflow操作MNIST數據集。

  MNIST數據集的官網是Yann LeCun's website。在這里,我們提供了一份python源代碼用于自動下載和安裝這個數據集。你可以下載這份代碼,然后用下面的代碼導入到你的項目里面,也可以直接復制粘貼到你的代碼文件里面。

  注意:MNIST數據集包括訓練集的圖片和標記數據,以及測試集的圖片和標記數據,在測試集包含的10000個樣例中,前5000個樣例取自原始的NIST訓練集,后5000個取自原始的NIST測試集,因此前5000個預測起來更容易些。

import tensorflow.examples.tutorials.mnist.input_data as input_data
mnist = input_data.read_data_sets("MNIST_data/", one_hot=True)

  當one-hot變為為TRUE,則只要對應的位置的值為1,其余都是0。

  這里,我直接下載了數據集,并放在了我的代碼里面。測試如下:

# _*_coding:utf-8_*_

from tensorflow.examples.tutorials.mnist import input_data

mnist = input_data.read_data_sets('MNIST_data', one_hot=True)
print("OK")
# 打印“Training data size: 55000”
print("Training data size: ", mnist.train.num_examples)
# 打印“Validating data size: 5000”
print("Validating data size: ", mnist.validation.num_examples)
# 打印“Testing data size: 10000”
print("Testing data size: ", mnist.test.num_examples)
# 打印“Example training data: [0. 0. 0. ... 0.380 0.376 ... 0.]”
print("Example training data: ", mnist.train.images[0])
# 打印“Example training data label: [0. 0. 0. 0. 0. 0. 0. 1. 0. 0.]”
print("Example training data label: ", mnist.train.labels[0])

batch_size = 100
# 從train的集合中選取batch_size個訓練數據
xs, ys = mnist.train.next_batch(batch_size)
# 輸出“X shape:(100,784)”
print("X shape: ", xs.shape)
# 輸出"Y shape:(100,10)"
print("Y shape: ", ys.shape)

  結果如下:

Extracting MNIST_data	rain-images-idx3-ubyte.gz
Extracting MNIST_data	rain-labels-idx1-ubyte.gz
Extracting MNIST_data	10k-images-idx3-ubyte.gz
Extracting MNIST_data	10k-labels-idx1-ubyte.gz
OK
Training data size:  55000
Validating data size:  5000
Testing data size:  10000
X shape:  (100, 784)
Y shape:  (100, 10)

  從上面的代碼可以看出,通過input_data.read_data_sets函數生成的類會自動將MNIST數據集劃分成train,validation和test三個數據集,其中train這個集合內含有55000張圖片,validation集合內含有5000張圖片,這兩個集合組成了MNIST本身提供的訓練數據集。test集合內有10000張圖片,這些圖片都來自與MNIST提供的測試數據集。處理后的每一張圖片是一個長度為784的一維數組,這個數組中的元素對應了圖片像素矩陣中的每一個數字(28*28=784)。因為神經網絡的輸入是一個特征向量,所以在此把一張二維圖像的像素矩陣放到一個一維數組中可以方便tensorflow將圖片的像素矩陣提供給神經網絡的輸入層。像素矩陣中元素的取值范圍為[0, 1],它代表了顏色的深淺。其中0表示白色背景,1表示黑色前景。

Softmax回歸的再復習

  我們知道MNIST的每一張圖片都表示一個數字,從0到9。我們希望得到給定圖片代表每個數字的概率。比如說,我們的模型可能推測一張包含9的圖片代表數字9 的概率為80%但是判斷它是8 的概率是5%(因為8和9都有上半部分的小圓),然后給予它代表其他數字的概率更小的值。

  這是一個使用softmax regression模型的經典案例。softmax模型可以用來給不同的對象分配概率。即使在之后,我們訓練更加精細的模型的時候,最后一步也是需要softmax來分配概率。

  Softmax回歸可以解決兩種以上的分類,該模型是Logistic回歸模型在分類問題上的推廣。對于要識別的0~9這10類數字,首選Softmax回歸。MNIST的Softmax回歸源代碼位于Tensorflow-1.1.0/tensorflow.examples/tutorials/mnist/mnist_softmax.py

softmax回歸第一步

  為了得到一張給定圖片屬于某個特定數字類的證據(evidence),我們對圖片像素值進行加權求和。如果這個像素具有很強的證據說明這種圖片不屬于該類,那么相應的權值為負數,相反如果這個像素擁有很強的證據說明這張圖片不屬于該類,那么相應的權值為負數,相反如果這個像素擁有有力的證據支持這張圖片屬于這個類,那么權值是正數。

  下面的圖片顯示了一個模型學習到的圖片上每個像素對于特定數字類的權值,紅色代表負數權值,藍色代表正數權值。

  我們也需要加入一個額外的偏置量(bias),因為輸入往往會帶有一些無關的干擾量。因為對于給定的輸入圖片 x 它代表的是數字 i 的證據可以表示為:

  其中,Wi代表權重,bi代表數字 i 類的偏置量, j 代表給定圖片 x 的像素索引用于像素求和。然后用 softmax 函數可以把這些證據轉換成概率 y:

  這里的softmax可以看成是一個激勵(activation)函數或者鏈接(link)函數,把我們定義的線性函數的輸出轉換成我們想要的格式,也就是關于10個數字類的概率分布。因此,給定一張圖片,它對于每一個數字的吻合度可以被softmax函數轉換成一個概率值。softmax函數可以定義為:

  展開等式右邊的子式,可以得到:

  但是更多的時候把softmax模型函數定義為前一種形式:把輸入值當做冪指函數求值,再正則化這些結果值。這個冪運算表示,更大的證據對應更大的假設模型(hypothesis)里面的乘數權重值。反之,擁有更少的證據意味著在假設模型里面擁有更小的乘數系數。假設模型里面的權值不可以是0值或者負值。Softmax然后會正則化這些權重值,使他們的總和等于1,以此構造一個有效的概率分布。

  對于softmax回歸模型可以用下面的圖解釋,對于輸入的 xs 加權求和,再分別加上一個偏置量,最后再輸入到 softmax 函數中:

  如果把它寫成一個等式,我們可以得到:

  我們也可以用向量表示這個計算過程:用矩陣乘法和向量相加。這有助于提高計算效率:

  更進一步,可以寫成更加緊湊的方式:

  接下來就是使用Tensorflow實現一個 Softmax Regression。其實在Python中,當還沒有TensorFlow時,通常使用Numpy做密集的運算操作。因為Numpy是使用C和一部分 fortran語言編寫的,并且調用了 openblas,mkl 等矩陣運算庫,因此效率很好。其中每一個運算操作的結果都要返回到Python中,但不同語言之間傳輸數據可能會帶來更大的延遲。TensorFlow同樣將密集的復雜運算搬到Python外面執行,不過做的更徹底。

  首先載入TensorFlow庫,并創建一個新的 InteractiveSession ,使用這個命令會將這個session 注冊為默認的session,之后的運算也默認跑在這個session里,不同session之間的數據和運算應該都是相互獨立的。接下來創建一個 placeholder,即輸入數據的地方。Placeholder 的第一個參數是數據類型,第二個參數 [None, 784] 代表 tensor 的 shape,也就是數據的尺寸,這里的None代表不限條數的輸入, 784代表每條數據的是一個784維的向量。

sess = tf.InteractiveSession()
x = tf.placeholder(tf.float32, [None, 784])

  接下來要給Softmax Regression模型中的 weights和 biases 創建 Variable 對象,Variable是用來存儲模型參數的,不同于存儲數據的 tensor 一旦使用掉就會消失,Variable 在模型訓練迭代中是持久的,它可以長期存在并且在每輪迭代中被更新。我們把 weights 和 biases 全部初始化為0,因為模型訓練時會自動學習適合的值,所以對這個簡單模型來說初始值不太重要,不過對于復雜的卷積網絡,循環網絡或者比較深的全連接網絡,初始化的方法就比較重要,甚至可以說至關重要。注意這里的W的shape是 [784, 10] 中 , 784是特征的維數,而后面的10 代表有10類,因為Label在 one-hot編碼后是10維的向量。

W = tf.Variable(tf.zeros([784, 10]))
b = tf.Variable(tf.zeros([10]))

  接下來要實現 Softmax Regression 算法,我們將上面提到的 y = softmax(Wx+b),改為 TensorFlow的語言就是下面的代碼:

y = tf.nn.softmax(tf.matmul(x, W) + b)

  Softmax是 tf.nn 下面的一個函數,而 tf.nn 則包含了大量的神經網絡的組件, tf.matmul是TensorFlow中矩陣乘法函數。我們就定義了簡單的 Softmax Regression,語法和直接寫數學公式很像。然而Tensorflow最厲害的地方還不是定義公式,而是將forward和backward的內容都自動實現。所以我們接下來定義好loss,訓練時將會自動求導并進行梯度下降,完成對 Softmax Regression模型參數的自動學習。

回歸模型訓練的步驟

  構建回歸模型,我們需要輸入原始真實值(group truth),計算采用softmax函數擬合后的預測值,并定義損失函數和優化器。

  為了訓練模型,我們首先需要定義一個指標來評估這個模型是好的。其實,在機器學習中,我們通常定義指標來表示一個模型是壞的,這個指標稱為成本(cost)或者損失(loss),然后近鄰最小化這個指標。但是這兩種方式是相同的。

  一個非常常見的的成本函數是“交叉熵(cross-entropy)”。交叉熵產生于信息論里面的信息壓縮編碼技術,但是后來他演變為從博弈論到機器學習等其他領域里的重要技術手段,它的定義如下:

  Y是我們預測的概率分布, y' 是實際的分布(我們輸入的 one-hot vector)。比較粗糙的理解是,交叉熵是用來衡量我們的預測用于描述真相的低效性。

  為了計算交叉熵,我們首先需要添加一個新的占位符用于輸入正確值:

y_ = tf.placeholder("float", [None,10])

  然后我們可以用交叉熵的公式計算交叉熵:

cross_entropy = -tf.reduce_sum(y_*tf.log(y))

  或者

cross_entropy = tf.reduce_mean(-tf.reduce_sum(y_ * tf.log(y),
                                              reduction_indices=[1]))

  (這里我們 y_ * tf.log(y)是前面公式的交叉熵公式,而 tf.reduce_sum就是求和,而tf.reduce_mean則是用來對每個 batch 的數據結果求均值。)

  首先,用 tf.log 計算 y 的每個元素的對數。接下來,我們把 y_ 的每一個元素 和 tf.log(y) 的對應元素相乘。最后用 tf.reduce_sum 計算張量的所有元素總和。(注意:這里的交叉熵不僅僅用來衡量單一的一對預測和真實值,而是所有的圖片的交叉熵的總和。對于所有的數據點的預測表現比單一數據點的表現能更好的描述我們的模型的性能)。

  當我們知道我們需要我們的模型做什么的時候,用TensorFlow來訓練它是非常容易的。因為TensorFlow擁有一張描述我們各個計算單元的圖,它可以自動的使用反向傳播算法(backpropagation algorithm)來有效的確定變量是如何影響想要最小化的那個成本值。然后TensorFlow會用選擇的優化算法來不斷地修改變量以降低成本。

train_step = tf.train.GradientDescentOptimizer(0.01).minimize(cross_entropy)

  在這里,我們要求TensorFlow用梯度下降算法(gradient descent alogrithm)以0.01的學習速率最小化交叉熵。梯度下降算法(gradient descent algorithm)是一個簡單的學習過程,TensorFlow只需將每個變量一點點地往使成本不斷降低的方向移動。當然TensorFlow也提供了其他許多優化算法:只要簡單地調整一行代碼就可以使用其他的算法。

  TensorFlow在這里實際上所做的是,它會在后臺給描述你的計算的那張圖里面增加一系列新的計算操作單元用于實現反向傳播算法和梯度下降算法。然后,它返回給你的只是一個單一的操作,當運行這個操作時,它用梯度下降算法訓練你的模型,微調你的變量,不斷減少成本。

  現在,我們已經設置好了我們的模型,在運行計算之前,我們需要添加一個操作來初始化我們創建的變量并初始化,但是我們可以在一個Session里面啟動我們的模型:

with tf.Session() as sess:
    tf.global_variables_initializer().run()

  然后開始訓練模型,這里我們讓模型訓練訓練1000次:

for i in range(1000):
  batch_xs, batch_ys = mnist.train.next_batch(100)
  sess.run(train_step, feed_dict={x: batch_xs, y_: batch_ys})

  或者

for i in range(1000):
    batch_xs, batch_ys = mnist.train.next_batch(100)
    train_step.run({x: batch_xs, y:batch_ys})

  該訓練的每個步驟中,我們都會隨機抓取訓練數據中的100個批處理數據點,然后我們用這些數據點作為參數替換之前的占位符來運行 train_step。而且我們可以通過 feed_dict 將 x 和 y_ 張量 占位符用訓練數據替代。(注意:在計算圖中,我們可以用 feed_dict 來替代任何張量,并不僅限于替換占位符)

  使用一小部分的隨機數據來進行訓練被稱為隨機訓練(stochastic training)- 在這里更確切的說是隨機梯度下降訓練。在理想情況下,我們希望用我們所有的數據來進行每一步的訓練,因為這能給我們更好的訓練結果,但顯然這需要很大的計算開銷。所以,每一次訓練我們可以使用不同的數據子集,這樣做既可以減少計算開銷,又可以最大化地學習到數據集的總體特性。

  現在我們已經完成了訓練,接下來就是對模型的準確率進行驗證,下面代碼中的 tf.argmax是從一個 tensor中尋找最大值的序號, tf.argmax(y, 1)就是求各個預測的數字中概率最大的一個,而 tf.argmax(y_, 1)則是找到樣本的真實數字類別。而 tf.equal方法則是用來判斷預測數字類別是否就是正確的類別,最后返回計算分類是否正確的操作 correct_predition。

訓練模型的步驟

  已經設置好了模型,在訓練之前,需要先初始化我們創建的變量,以及在會話中啟動模型。

# 這里使用InteractiveSession() 來創建交互式上下文的TensorFlow會話
# 與常規會話不同的時,交互式會話成為默認會話方法
# 如tf.Tensor.eval 和 tf.Operation.run 都可以使用該會話來運行操作(OP
sess = tf.InteractiveSession()
tf.global_variables_initializer().run()

  我們讓模型循環訓練1000次,在每次循環中我們都隨機抓取訓練數據中100個數據點,來替代之前的占位符。

# Train
for _ in range(1000):
    batch_xs, batch_ys = mnist.train.next_batch(100)
    sess.run(train_step, feed_dict={x:batch_xs, y_:batch_ys})

  這種訓練方式成為隨機訓練(stochastic training),使用SGD方法進行梯度下降,也就是每次從訓練數據集中抓取一小部分數據進行梯度下降訓練。即與每次對所有訓練數據進行計算的BGD相比,SGD即能夠學習到數據集的總體特征,又能夠加速訓練過程。

保存檢查點(checkpoint)的步驟

  為了得到可以用來后續恢復模型以進一步訓練或評估的檢查點文件(checkpoint file),我們實例化一個 tf.train.Saver。

saver = tf.train.Saver()

  在訓練循環中,將定期調用 saver.save() 方法,向訓練文件夾中寫入包含了當前所有可訓練變量值的檢查點文件。

saver.save(sess, FLAGS.train_dir, global_step=step)

  這樣,我們以后就可以使用 saver.restore() 方法,重載模型的參數,繼續訓練。

saver.restore(sess, FLAGS.train_dir)

  這里不多講,具體請參考博客:TensorFlow學習筆記:模型持久化的原理。。

評估模型的步驟

  那么我們的模型性能如何呢?

  首先讓我們找出那些預測正確的標簽。tf.argmax 是一個非常有用的函數,它能給出某個 tensor 對象在某一維上的其數據最大值所在的索引值。由于標簽向量是由0, 1 組成,所以最大值1所在的索引位置就是類別標簽,比如 tf.argmax(y, 1) 返回的是模型對于任一輸入 x 預測到的標簽值,而 tf.argmax(y_, 1) 代表正確的標簽,我們可以用 tf.equal 來檢測我們的預測是否真實標簽匹配(索引位置一樣表示匹配)。

correct_prediction = tf.equal(tf.argmax(y,1), tf.argmax(y_,1))

  這里返回一個布爾數組,為了計算我們分類的準確率,我們使用cast將布爾值轉換為浮點數來代表對,錯,然后取平均值。例如: [True, False, True, True] 變為 [1, 0, 1, 1],計算出平均值為 0.75。

accuracy = tf.reduce_mean(tf.cast(correct_prediction, "float"))

  最后,我們可以計算出在測試數據上的準確率,大概是91%。

print(accuracy.eval(feed_dict={x: mnist.test.images, y_: mnist.test.labels}))

  完整代碼如下:

  下面我們總結一下實現了上面簡單機器學習算法 Softmax Regression,這可以算作是一個沒有隱含層的最淺的神經網絡,我們來總結一下整個流程,我們做的事情分為下面四部分:

1,定義算法公式,也就是神經網絡forward時的計算
2,定義loss,選定優化器,并指定優化器優化loss
3,迭代地對數據進行訓練
4,在測試集或驗證集上對準確率進行評測

  這幾個步驟是我們使用Tensorflow進行算法設計,訓練的核心步驟,也將會貫穿所有神經網絡。

  代碼如下:

#_*_coding:utf-8_*_
import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_data

# 首先我們直接加載TensorFlow封裝好的MNIST數據   28*28=784
mnist = input_data.read_data_sets('MNIST_data/', one_hot=True)
print(mnist.train.images.shape, mnist.train.labels.shape)
print(mnist.test.images.shape, mnist.test.labels.shape)
print(mnist.validation.images.shape, mnist.validation.labels.shape)
'''
(55000, 784) (55000, 10)
(10000, 784) (10000, 10)
(5000, 784) (5000, 10)
'''

sess = tf.InteractiveSession()
x = tf.placeholder(tf.float32, [None, 784])

W = tf.Variable(tf.zeros([784, 10]))
b = tf.Variable(tf.zeros([10]))

y = tf.nn.softmax(tf.matmul(x, W) + b)

y_ = tf.placeholder(tf.float32, [None, 10])
cross_entropy = tf.reduce_mean(-tf.reduce_sum(y_ * tf.log(y),
                                              reduction_indices=[1]))

train_step = tf.train.GradientDescentOptimizer(0.5).minimize(cross_entropy)

tf.global_variables_initializer().run()

for i in range(1000):
    batch_xs, batch_ys = mnist.train.next_batch(100)
    train_step.run({x: batch_xs, y_: batch_ys})

correct_prediction = tf.equal(tf.argmax(y, 1), tf.argmax(y_, 1))

accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))

print(accuracy.eval(feed_dict={x: mnist.test.images, y_: mnist.test.labels}))

  

傳統神經網絡訓練MNIST數據

1,訓練步驟

輸入層:拉伸的手寫文字圖像,維度為 [-1, 28*28]
全連接層:500個節點,維度為[-1, 500]
輸出層:分類輸出,維度為 [-1, 10]

2,訓練代碼及其結果

  傳統的神經網絡訓練代碼:

# _*_coding:utf-8_*_
import tensorflow as tf
import numpy as np
from tensorflow.examples.tutorials.mnist import input_data

BATCH_SIZE = 100  # 訓練批次
INPUT_NODE = 784  # 784=28*28 輸入節點
OUTPUT_NODE = 10  # 輸出節點
LAYER1_NODE = 784  # 層級節點
TRAIN_STEP = 10000  # 訓練步數
LEARNING_RATE = 0.01
L2NORM_RATE = 0.001


def train(mnist):
    # define input placeholder
    # None表示此張量的第一個維度可以是任何長度的
    input_x = tf.placeholder(tf.float32, [None, INPUT_NODE], name='input_x')
    input_y = tf.placeholder(tf.float32, [None, OUTPUT_NODE], name='input_y')

    # define weights and biases
    w1 = tf.Variable(tf.truncated_normal(shape=[INPUT_NODE, LAYER1_NODE], stddev=0.1))
    b1 = tf.Variable(tf.constant(0.1, shape=[LAYER1_NODE]))

    w2 = tf.Variable(tf.truncated_normal(shape=[LAYER1_NODE, OUTPUT_NODE], stddev=0.1))
    b2 = tf.Variable(tf.constant(0.1, shape=[OUTPUT_NODE]))

    layer1 = tf.nn.relu(tf.nn.xw_plus_b(input_x, w1, b1))
    y_hat = tf.nn.xw_plus_b(layer1, w2, b2)
    print("1 step ok!")

    # define loss
    cross_entropy = tf.nn.softmax_cross_entropy_with_logits(logits=y_hat, labels=input_y)
    cross_entropy_mean = tf.reduce_mean(cross_entropy)
    regularization = tf.nn.l2_loss(w1) + tf.nn.l2_loss(w2) + tf.nn.l2_loss(b1) + tf.nn.l2_loss(b2)
    loss = cross_entropy_mean + L2NORM_RATE * regularization
    print("2 step ok")

    # define accuracy
    correct_predictions = tf.equal(tf.argmax(y_hat, 1), tf.argmax(input_y, 1))
    accuracy = tf.reduce_mean(tf.cast(correct_predictions, tf.float32))
    print("3 step ok")

    # train operation
    global_step = tf.Variable(0, trainable=False)
    train_op = tf.train.AdamOptimizer(LEARNING_RATE).minimize(loss, global_step=global_step)
    print("4 step ok ")

    with tf.Session() as sess:
        tf.global_variables_initializer().run()  # 初始化創建的變量
        print("5 step OK")

        # 開始訓練模型
        for i in range(TRAIN_STEP):
            xs, ys = mnist.train.next_batch(BATCH_SIZE)
            feed_dict = {
                input_x: xs,
                input_y: ys,
            }
            _, step, train_loss, train_acc = sess.run([train_op, global_step, loss, accuracy], feed_dict=feed_dict)
            if (i % 100 == 0):
                print("After %d steps, in train data, loss is %g, accuracy is %g." % (step, train_loss, train_acc))

        test_feed = {input_x: mnist.test.images, input_y: mnist.test.labels}
        test_acc = sess.run(accuracy, feed_dict=test_feed)
        print("After %d steps, in test data, accuracy is %g." % (TRAIN_STEP, test_acc))


if __name__ == '__main__':
    mnist = input_data.read_data_sets("data", one_hot=True)
    print("0 step ok!")
    train(mnist)

  結果如下:

Extracting data	rain-images-idx3-ubyte.gz
Extracting data	rain-labels-idx1-ubyte.gz
Extracting data	10k-images-idx3-ubyte.gz
Extracting data	10k-labels-idx1-ubyte.gz
0 step ok!
1 step ok!
2 step ok
3 step ok
4 step ok 
5 step OK
After 1 steps, in train data, loss is 5.44352, accuracy is 0.03.
After 101 steps, in train data, loss is 0.665012, accuracy is 0.95.

...  ...

After 9901 steps, in train data, loss is 0.304703, accuracy is 0.96.
After 10000 steps, in test data, accuracy is 0.9612.

  

3,部分代碼解析:

占位符

  我們通過為輸入圖像和目標輸出類別創建節點,來開始構建計算圖:

x = tf.placeholder("float", [None, 784])

  x 不是一個特定的值,而是一個占位符 placeholder,我們在TensorFlow運行計算時輸入這個值我們希望能夠輸入任意數量的MNIST圖像,每張圖展平為784維(28*28)的向量。我們用二維的浮點數張量來表示這些圖,這個張量的形狀是 [None, 784],這里的None表示此張量的第一個維度可以是任意長度的。

  雖然palceholder 的shape參數是可選的,但是有了它,TensorFlow能夠自動捕捉因數據維度不一致導致的錯誤。

變量

  一個變量代表著TensorFlow計算圖中的一個值,能夠在計算過程中使用,甚至進行修改。在機器學習的應用過程中,模型參數一般用Variable來表示。

w1 = tf.Variable(tf.truncated_normal(shape=[INPUT_NODE, LAYER1_NODE], stddev=0.1))
b1 = tf.Variable(tf.constant(0.1, shape=[LAYER1_NODE]))

  我們的模型也需要權重值和偏置量,當然我們可以將其當做是另外的輸入(使用占位符),這里使用Variable。一個Variable代表一個可修改的張量,它可以用于計算輸入值,也可以在計算中被修改。對于各種機器學習英語,一般都會有模型參數,可以用Variable來表示。

  變量需要通過session初始化后,才能在session中使用,這一初始化的步驟為,為初始化指定具體值,并將其分配給每個變量,可以一次性為所有變量完成此操作。

sess.run(tf.initialize_all_variables())

  

卷積神經網絡-CNN訓練MNIST數據

  在MNIST上只有96%的正確率是在是糟糕,所以這里我們用一個稍微復雜的模型:卷積神經網絡來改善效果,這里大概會達到100%的準確率。

  同樣,構建的流程也是先加載數據,再構架網絡模型,最后訓練和評估模型。

1,訓練步驟

輸入層:手寫文字圖像,維度為[-1, 28, 28, 1]
卷積層1:filter的shape為5*5*32,strides為1,padding為“SAME”。卷積后維度為 [-1, 28, 28, 32]
池化層2:max-pooling,ksize為2*2, 步長為2,池化后維度為 [-1, 14, 14, 32]
卷積層3:filter的shape為5*5*64,strides為1,padding為“SAME”。卷積后維度為 [-1, 14, 14, 64]
池化層4:max-pooling,ksize為2*2, 步長為2。池化后維度為 [-1, 7, 7, 64]
池化后結果展開:將池化層4 feature map展開, [-1, 7, 7, 64] ==> [-1, 3136]
全連接層5:輸入維度 [-1, 3136],輸出維度 [-1, 512]
全連接層6:輸入維度 [-1, 512],輸出維度[-1, 10]。經過softmax后,得到分類結果。

2,構建模型

  構建一個CNN模型,需要以下幾步。

  (1)定義輸入數據并預處理數據。這里,我們首先讀取數據MNIST,并分別得到訓練集的圖片和標記的矩陣,以及測試集的圖片和標記的矩陣。代碼如下:

# _*_coding:utf-8_*_
import tensorflow as tf
import numpy as np
from tensorflow.examples.tutorials.mnist import input_data

mnist = input_data.read_data_sets('MNIST_data', one_hot=True)
trX, trY = mnist.train.images, mnist.train.labels
teX, teY = mnist.test.images, mnist.test.labels

  其中,mnist是TensorFlow的TensorFlow.contrib.learn中的Datasets,其值如下:

  trX, trY, teX, teY 是數據的矩陣表現,其值類似于:

  接著,需要處理輸入的數據,把上面 trX 和 teX 的形狀變為 [-1, 28, 28, 1], -1表示不考慮輸入圖片的數量,28*28表示圖片的長和寬的像素數,1是通道(channel)數量,因為MNIST的圖片是黑白的,所以通道為1,如果是RGB彩色圖像,則通道為3 。

trX = trX.reshape(-1, 28, 28, 1)   # 28*28*1  input image
teX = teX.reshape(-1, 28, 28, 1)   # 28*28*1  input image

X = tf.placeholder('float', [None, 28, 28, 1])
Y = tf.placeholder('float', [None, 10])

  (2)初始化權重與定義網絡結構。這里,我們將要構建一個擁有3個卷積和3個池化層,隨后一個全連接層,再接一個輸出層的卷積神經網絡。

  首先定義初始化權重的函數:

def init_weights(shape):
    return tf.Variable(tf.random_normal(shape, stddev=0.01))


# 初始化權重方法如下,我們設置卷積核的大小為3*3
w = init_weights([3, 3, 1, 32])  # patch大小為3*3,輸入維度為1,輸出維度為32
w2 = init_weights([3, 3, 32, 64])  # patch大小為3*3,輸入維度為32,輸出維度為64
w3 = init_weights([3, 3, 64, 128])  # patch大小為3*3,輸入維度為64,輸出維度為128
# 全連接層,輸入維度為128*4*4,是上一層的輸出數據又三維的轉變成一維,輸出維度為625
w4 = init_weights([128 * 4 * 4, 625])
# 輸出層,輸入維度為625  輸出維度為10, 代表10類
w_o = init_weights([625, 10])

  隨后,定義一個模型函數,代碼如下:

# 神經網絡模型的構建函數,傳入以下參數
# X:輸入數據
# w:每一層的權重
# p_keep_conv , p_keep_hidden, dropout 要保留的神經元比例

def model(X, w, w2, w3, w4, w_0, p_keep_conv, p_keep_hidden):
    # 第一組卷積層以及池化層,最后dropout一些神經元
    l1a = tf.nn.relu(tf.nn.conv2d(X, w, strides=[1, 1, 1, 1], padding='SAME'))
    # l1a  shape=(?, 28, 28, 32)
    l1 = tf.nn.max_pool(l1a, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME')
    # l1 shape=(?, 14, 14, 32)
    l1 = tf.nn.dropout(l1, p_keep_conv)

    # 第二組卷積層以及池化層,最后dropout一些神經元
    l2a = tf.nn.relu(tf.nn.conv2d(l1, w2, strides=[1, 1, 1, 1], padding='SAME'))
    # l2a  shape=(?, 14, 14, 64)
    l2 = tf.nn.max_pool(l2a, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME')
    # l2  shape=(?, 7, 7, 64)
    l2 = tf.nn.dropout(l2, p_keep_conv)

    # 第三組卷積層以及池化層,最后dropout一些神經元
    l3a = tf.nn.relu(tf.nn.conv2d(l2, w3, strides=[1, 1, 1, 1], padding='SAME'))
    # l3a  shape=(?, 7, 7, 128)
    l3 = tf.nn.max_pool(l3a, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME')
    # l3  shape=(?, 4, 4, 128)
    l3 = tf.reshape(l3, [-1, w4.get_shape().as_list()[0]])  # reshape to (?, 2048)
    l3 = tf.nn.dropout(l3, p_keep_conv)
    
    # 全連接層,最后dropout一些神經元
    l4 = tf.nn.relu(tf.matmul(l3, w4))
    l4 = tf.nn.dropout(l4, p_keep_hidden)
    
    # 輸出層
    pyx = tf.matmul(l4, w_o)
    return pyx   # 返回預測值

  我們定義dropout的占位符——keep_conv,它表示在一層中有多少比例的神經元被保留下來,生成網絡模型,得到預測值,如下:

p_keep_cobv = tf.placeholder('float')
p_keep_hidden = tf.placeholder('float')
# 得到預測值
py_x = model(X, w, w2, w3, w4, w_o, p_keep_cobv, p_keep_hidden)

  接下來,定義頓時函數,這里我們仍然采用 tf.nn.softmax_cross_entropy_with_logits 來作比較預測值和真實值的差異,并做均值處理,定義訓練的操作(train_op),采用實現 RMSProp算法的優化器 tf.train.RMSPropOptimizer,學習率為0.001,衰減值為0.9,使損失最小;定義預測的操作(predict_op)。具體如下:

cost = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(logits=py_x, labels=Y))
train_op = tf.train.RMSPropOptimizer(0.001, 0.9).minimize(cost)
predict_op = tf.argmax(py_x, 1)

  (3)定義訓練時的批次大小和評估時的批次大小,如下:

batch_size = 128
test_size = 256

# 在一個會話中啟動圖,開始訓練和評估
# Launch the graph in a session
with tf.Session() as sess:
    # you need to initilaize all variables
    tf.global_variables_initializer().run()

    for i in range(100):
        training_batch = zip(range(0, len(trX), batch_size),
                             range(batch_size, len(trX) + 1, batch_size))
        for start, end in training_batch:
            sess.run(train_op, feed_dict={
                X: trX[start:end], Y: trY[start:end],
                p_keep_conv: 0.8,
                p_keep_hidden: 0.5
            })

        test_indices = test_indices[0: test_size]
        print(i, np.mean(np.argmax(teY[test_indices], axis=1) ==
                         sess.run(predict_op, feed_dict={X: teX[test_indices],
                                                         p_keep_conv: 1.0,
                                                         p_keep_hidden: 1.0})))

  完整代碼如下:

# _*_coding:utf-8_*_
import tensorflow as tf
import numpy as np
from tensorflow.examples.tutorials.mnist import input_data

mnist = input_data.read_data_sets('MNIST_data/', one_hot=True)
trX, trY = mnist.train.images, mnist.train.labels
teX, teY = mnist.test.images, mnist.test.labels

trX = trX.reshape(-1, 28, 28, 1)  # 28*28*1  input image
teX = teX.reshape(-1, 28, 28, 1)  # 28*28*1  input image

X = tf.placeholder('float', [None, 28, 28, 1])
Y = tf.placeholder('float', [None, 10])


def init_weights(shape):
    return tf.Variable(tf.random_normal(shape, stddev=0.01))


# 初始化權重方法如下,我們設置卷積核的大小為3*3
w = init_weights([3, 3, 1, 32])  # patch大小為3*3,輸入維度為1,輸出維度為32
w2 = init_weights([3, 3, 32, 64])  # patch大小為3*3,輸入維度為32,輸出維度為64
w3 = init_weights([3, 3, 64, 128])  # patch大小為3*3,輸入維度為64,輸出維度為128
# 全連接層,輸入維度為128*4*4,是上一層的輸出數據又三維的轉變成一維,輸出維度為625
w4 = init_weights([128 * 4 * 4, 625])
# 輸出層,輸入維度為625  輸出維度為10, 代表10類
w_o = init_weights([625, 10])


# 神經網絡模型的構建函數,傳入以下參數
# X:輸入數據
# w:每一層的權重
# p_keep_conv , p_keep_hidden, dropout 要保留的神經元比例

def model(X, w, w2, w3, w4, w_0, p_keep_conv, p_keep_hidden):
    # 第一組卷積層以及池化層,最后dropout一些神經元
    l1a = tf.nn.relu(tf.nn.conv2d(X, w, strides=[1, 1, 1, 1], padding='SAME'))
    # l1a  shape=(?, 28, 28, 32)
    l1 = tf.nn.max_pool(l1a, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME')
    # l1 shape=(?, 14, 14, 32)
    l1 = tf.nn.dropout(l1, p_keep_conv)

    # 第二組卷積層以及池化層,最后dropout一些神經元
    l2a = tf.nn.relu(tf.nn.conv2d(l1, w2, strides=[1, 1, 1, 1], padding='SAME'))
    # l2a  shape=(?, 14, 14, 64)
    l2 = tf.nn.max_pool(l2a, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME')
    # l2  shape=(?, 7, 7, 64)
    l2 = tf.nn.dropout(l2, p_keep_conv)

    # 第三組卷積層以及池化層,最后dropout一些神經元
    l3a = tf.nn.relu(tf.nn.conv2d(l2, w3, strides=[1, 1, 1, 1], padding='SAME'))
    # l3a  shape=(?, 7, 7, 128)
    l3 = tf.nn.max_pool(l3a, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME')
    # l3  shape=(?, 4, 4, 128)
    l3 = tf.reshape(l3, [-1, w4.get_shape().as_list()[0]])  # reshape to (?, 2048)
    l3 = tf.nn.dropout(l3, p_keep_conv)

    # 全連接層,最后dropout一些神經元
    l4 = tf.nn.relu(tf.matmul(l3, w4))
    l4 = tf.nn.dropout(l4, p_keep_hidden)

    # 輸出層
    pyx = tf.matmul(l4, w_o)
    return pyx  # 返回預測值


p_keep_conv = tf.placeholder('float')
p_keep_hidden = tf.placeholder('float')
# 得到預測值
py_x = model(X, w, w2, w3, w4, w_o, p_keep_conv, p_keep_hidden)

cost = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(logits=py_x, labels=Y))
train_op = tf.train.RMSPropOptimizer(0.001, 0.9).minimize(cost)
predict_op = tf.argmax(py_x, 1)

batch_size = 128
test_size = 256

# 在一個會話中啟動圖,開始訓練和評估
# Launch the graph in a session
with tf.Session() as sess:
    # you need to initilaize all variables
    tf.global_variables_initializer().run()

    for i in range(100):
        training_batch = zip(range(0, len(trX), batch_size),
                             range(batch_size, len(trX) + 1, batch_size))
        for start, end in training_batch:
            sess.run(train_op, feed_dict={
                X: trX[start:end], Y: trY[start:end],
                p_keep_conv: 0.8,
                p_keep_hidden: 0.5
            })

        test_indices = test_indices[0: test_size]
        print(i, np.mean(np.argmax(teY[test_indices], axis=1) ==
                         sess.run(predict_op, feed_dict={X: teX[test_indices],
                                                         p_keep_conv: 1.0,
                                                         p_keep_hidden: 1.0})))

  

3,部分代碼解析:

初始化權重

  為了不在建立模型的時候反復做初始化的操作,我們定義兩個函數用于初始化。

# 定義W初始化函數
def get_weights(shape):
    form = tf.truncated_normal(shape, stddev=0.1)
    return tf.Variable(form)


# 定義b初始化函數
def get_biases(shape):
    form = tf.constant(0.1, shape=shape)
    return tf.Variable(form)

  為了創建此模型,我們需要創建大量的權重和偏置項。這個模型中的權重在初始化時應該加入少量的噪聲來打破對稱性以及避免0梯度。由于我們使用的是ReLU神經元,因此比較好的做法是用一個較小的正數來初始化偏置項,以避免神經元節點輸出恒為0 的問題(dead neurons)。為了不在建立模型的時候反復做初始化操作,我們定義兩個函數用于初始化。

卷積和池化

  TensorFlow在卷積和池化上有很強的靈活性,那么如何處理邊界?步長應該設置多大?這里我們卷積使用的是1步長(stride size),0邊距(padding size)。保證輸出和輸出的大小相同,我們的池化是用簡單的2*2大小的模板做 max pooling 。

第一層卷積

  現在我們開始實現第一層卷積,它是由一個卷積接一個 max pooling 完成。卷積在每個5*5的patch中算出 32個特征。卷積的權重張量形狀是 [5, 5, 1, 32],前兩個維度是patch的大小,接著是輸出通道的數目,最后是輸出的通道數目。為了使用這一層卷積,我們將x 變成一個4d向量,其第二,第三對應圖片的寬,高。最后一維代表輸出的通道數目。

  最后我們將x 和權重向量進行卷積,加上偏置項,然后應用ReLU激活函數,代碼如下:

# 第一層:卷積層conv1
'''
input: [-1, 28, 28, 1]
filter: [5, 5, 32]
output: [-1, 28, 28, 32]
'''
with tf.name_scope("conv1"):
    w = get_weights([FILTER1_SIZE, FILTER1_SIZE, 1, FILTER1_NUM])
    b = get_biases([FILTER1_NUM])
    conv1_op = tf.nn.conv2d(
        input=input_x,
        filter=w,
        strides=[1, 1, 1, 1],
        padding="SAME",
        name='conv1_op'
    )

    conv1 = tf.nn.relu(tf.nn.bias_add(conv1_op, b), name='relu')

  

第二層池化

   前面也說過,我們的池化用簡單傳統的2*2大小模板做 max pooling .

# 第二層:池化層pooling2
'''
input: [-1, 28, 28, 32]
output: [-1. 14, 14, 32]
'''
with tf.name_scope('pooling2'):
    pooling2 = tf.nn.max_pool(
        value=conv1,
        ksize=[1, 2, 2, 1],
        strides=[1, 2, 2, 1],
        padding='SAME',
        name='pooling1'
    )

  

第三層卷積

  為了構建一個更深的網絡,我們會把幾個類似的層堆疊起來,第二層中,每個5*5的patch 會得到64個特征,代碼如下:

# 第三次:卷積層conv3
'''
input" [-1, 14, 14, 32]
filter: [5, 5, 64]
output: [-1, 14, 14, 64]
'''
with tf.name_scope('conv3'):
    w = get_weights([FILTER3_SIZE, FILTER3_SIZE, FILTER1_NUM, FILTER3_NUM])
    b = get_biases([FILTER3_NUM])
    conv3_op = tf.nn.conv2d(
        input=pooling2,
        filter=w,
        strides=[1, 1, 1, 1],
        padding="SAME",
        name='conv3_op'
    )
    conv3 = tf.nn.relu(tf.nn.bias_add(conv3_op, b), name='relu')

  

第四層池化

  然后進行池化,隨機減少特征:

# 第四層:池化層pooling4
    '''
    input: [-1, 14, 14, 64]
    output: [-1, 7, 7, 64]
    '''
    with tf.name_scope("pooling4"):
        pooling4 = tf.nn.max_pool(
            value=conv3,
            ksize=[1, 2, 2, 1],
            strides=[1, 2, 2, 1],
            padding="SAME",
            name="pooling4"
        )

  

全連接層

  現在圖片的尺寸減少到7*7,我們加入一個3136個神經元的全連接層,用于處理整個圖片。我們先把池化層輸出的張量reshape成一些向量,也就是展開它。然后輸入其權重公式,偏置項公式,然后求其L2_Loss函數,最后代碼如下:

# 池化結果展開
'''
input: [-1, 7,7,64]
output: [-1, 3136]
'''
pooling4_flat = tf.reshape(pooling4, [-1, FLAT_SIZE])
print("5 step ok!")

# 第五層:全連接層 FC5
'''
input: [-1, 3136]  (56*56=3136)
output: [-1, 512]
'''
with tf.name_scope('fc5'):
    w = get_weights([FLAT_SIZE, FC5_SIZE])
    b = get_biases([FC5_SIZE])
    fc5 = tf.nn.relu(tf.nn.xw_plus_b(pooling4_flat, w, b, name='fc5'), name='relu')
    fc5_drop = tf.nn.dropout(fc5, droput_keep_prob)
    l2_loss += tf.nn.l2_loss(w) + tf.nn.l2_loss(b)
print("6 step OK")

# 第六層:全連接層(輸出)
'''
input: [-1, 512]
output: [-1, 10]
'''
with tf.name_scope('fc6'):
    w = get_weights([FC5_SIZE, OUTPUT_SIZE])
    b = get_biases([OUTPUT_SIZE])
    y_hat = tf.nn.xw_plus_b(fc5_drop, w, b, name='y_hat')
    l2_loss += tf.nn.l2_loss(w) + tf.nn.l2_loss(b)
print("7 step ok!")

cross_entropy = tf.nn.softmax_cross_entropy_with_logits(logits=y_hat, labels=input_y)
cross_entropy_mean = tf.reduce_mean(cross_entropy)
loss = cross_entropy_mean + L2NORM_RATE * l2_loss

  

簡易前饋神經網絡訓練MNIST數據

  下面利用TensorFlow使用MNIST數據集訓練并評估一個用于識別手寫數字的簡易前饋神經網絡(feed-forward neural network)。

1,代碼如下:

  其中說明一下下面兩個文件的目的:

1,full_connected_mnist.py 構建一個完全連接(fully connected)的MNIST模型所需的代碼
2,full_connected_feed.py 利用下載的數據集訓練構建好的MNIST模型的主要代碼

  full_connected_mnist.py代碼如下:

# _*_coding:utf-8_*_
"""Builds the MNIST network.
# 為模型構建實現推理 損失 訓練的模型
Implements the inference/loss/training pattern for model building.
# 推理  根據運行網絡的需要 構建模型向前做預測
1. inference() - Builds the model as far as required for running the network forward to make predictions.
#  將生成loss所需的層添加到推理模型中
2. loss() - Adds to the inference model the layers required to generate loss.
# 訓練  將生成和所需的ops添加到損失模型中并應用梯度
3. training() - Adds to the loss model the Ops required to generate and apply gradients.
This file is used by the various "fully_connected_*.py" files and not meant to
be run.
"""
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function

import math

import tensorflow as tf

# The MNIST dataset has 10 classes representing the digits 0 through 9
NUM_CLASSES = 10

# The MNIST images are always 28*28 pixels
IMAGES_SIZE = 28
IMAGES_PIXELS = IMAGES_SIZE * IMAGES_SIZE


def inference(images, hidden1_units, hidden2_units):
    """Build the MNIST model up to where it may be used for inference.
  Args:
    images: Images placeholder, from inputs().
    hidden1_units: Size of the first hidden layer.
    hidden2_units: Size of the second hidden layer.
  Returns:
    softmax_linear: Output tensor with the computed logits.
  """
    # hidden1
    with tf.name_scope('hidden1'):
        weights = tf.Variable(
            tf.truncated_normal([IMAGES_PIXELS, hidden1_units],
                                stddev=1.0 / math.sqrt(float(IMAGES_PIXELS))),
            name='weights'
        )
        biases = tf.nn.relu(tf.matmul([hidden1_units]),
                            name='weights')
        hidden1 = tf.nn.relu(tf.matmul(images, weights) + biases)
    # hidden2
    with tf.name_scope('hidden2'):
        weights = tf.Variable(
            tf.truncated_normal([hidden1_units, hidden2_units],
                                stddev=1.0 / math.sqrt(float(hidden1_units))),
            name='weights'
        )
        biases = tf.Variable(tf.zeros([hidden2_units]),
                             name='biases')
        hidden2 = tf.nn.relu(tf.matmul(hidden1, weights) + biases)
    # Linear
    with tf.name_scope('softmax_linear'):
        weights = tf.Variable(
            tf.truncated_normal([hidden2_units, NUM_CLASSES],
                                stddev=1.0 / math.sqrt(float(hidden2_units))),
            name='weights'
        )
        biases = tf.nn.relu(tf.zeros([NUM_CLASSES]),
                            name='biases')
        logits = tf.matmul(hidden2, weights) + biases
    return logits


def loss(logits, labels):
    """Calculates the loss from the logits and the labels.
  Args:
    logits: Logits tensor, float - [batch_size, NUM_CLASSES].
    labels: Labels tensor, int32 - [batch_size].
  Returns:
    loss: Loss tensor of type float.
  """
    labels = tf.to_int64(labels)
    return tf.losses.sparse_softmax_cross_entropy(labels=labels, logits=logits)


def training(loss, learning_rate):
    """Sets up the training Ops.
  Creates a summarizer to track the loss over time in TensorBoard.
  Creates an optimizer and applies the gradients to all trainable variables.
  The Op returned by this function is what must be passed to the
  `sess.run()` call to cause the model to train.
  Args:
    loss: Loss tensor, from loss().
    learning_rate: The learning rate to use for gradient descent.
  Returns:
    train_op: The Op for training.
  """
    # add a scalar summary for the snapshot loss
    tf.summary.scalar('loss', loss)
    # create the gradient descent optimizer with the given learning rate
    optimizer = tf.train.GradientDescentOptimizer(learning_rate)
    # create a variable to track the global step
    global_step = tf.Variable(0, name='global_step', trainable=False)
    # Use the optimizer to apply the gradients that minimize the loss
    # (and also increment the global step counter) as a single training step.
    train_op = optimizer.minimize(loss, global_step=global_step)
    return train_op


def evaluation(logits, labels):
    """Evaluate the quality of the logits at predicting the label.
    Args:
      logits: Logits tensor, float - [batch_size, NUM_CLASSES].
      labels: Labels tensor, int32 - [batch_size], with values in the
        range [0, NUM_CLASSES).
    Returns:
      A scalar int32 tensor with the number of examples (out of batch_size)
      that were predicted correctly.
    """
    # For a classifier model, we can use the in_top_k Op.
    # It returns a bool tensor with shape [batch_size] that is true for
    # the examples where the label is in the top k (here k=1)
    # of all logits for that example.
    correct = tf.nn.in_top_k(logits, labels, 1)
    # Return the number of true entries.
    return tf.reduce_sum(tf.cast(correct, tf.int32))

  full_connected_feed.py代碼如下:

#_*_coding:utf-8_*_
'''Trains and Evaluates the MNIST  network using a feed dictionary'''
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function

# pulint:disable=missing-docstring
import argparse
import os
import sys
import time

from six.moves import xrange   # pylint: disable=redefined-builtin
import tensorflow as tf

from tensorflow.examples.tutorials.mnist import input_data
from tensorflow.examples.tutorials.mnist import mnist

# Basic model parameters as external flags
FLAGS = None

def placeholder_inputs(batch__size):
    '''
        生成一個占位符變量來表述輸入張量
        這些占位符被模型構建的其余部分用作輸入代碼,并將從下面 .run() 循環中下載數據提供
    :param batch__size:
    :return: 返回一個 圖像的占位符,一個標簽占位符
    '''
    # Note that the shapes of the placeholders match the shapes of the full
    # image and label tensors, except the first dimension is now batch_size
    # rather than the full size of the train or test data sets.
    images_placeholder = tf.placeholder(tf.float32, shape=(batch__size, mnist.IMAGE_PIXELS))
    labels_placeholder = tf.placeholder(tf.int32, shape=(batch__size))
    return images_placeholder, labels_placeholder

def fill_feed_dict(data_set, images_pl, labels_pl):
    """Fills the feed_dict for training the given step.
      A feed_dict takes the form of:
      feed_dict = {
          <placeholder>: <tensor of values to be passed for placeholder>,
          ....
      }
      Args:
        data_set: The set of images and labels, from input_data.read_data_sets()
        images_pl: The images placeholder, from placeholder_inputs().
        labels_pl: The labels placeholder, from placeholder_inputs().
      Returns:
        feed_dict: The feed dictionary mapping from placeholders to values.
      """
    # Create the feed_dict for the placeholders filled with the next
    # `batch size` examples.
    images_feed, labels_feed = data_set.next_batch(FLAGS.batch_size, FLAGS.fake_data)
    feed_dict = {
        images_pl: images_feed,
        labels_pl: labels_feed,
    }
    return feed_dict

def do_evaluation(sess, eval_correct, images_placeholder, labels_placeholder, data_set):
    '''
        對陣個數據進行評估
    :param sess: The session in which the model has been trained.
    :param eval_correct: The Tensor that returns the number of correct predictions.
    :param images_placeholder: The images placeholder.
    :param labels_placeholder: The labels placeholder.
    :param data_set: The set of images and labels to evaluate, from
      input_data.read_data_sets().
    :return:
    '''
    # and run one epoch of eval
    true_count = 0  # counts the number of correct predictions
    steps_per_epoch = data_set.num_examples // FLAGS.batch_size
    num_examples = steps_per_epoch * FLAGS.batch_size
    for step in range(steps_per_epoch):
        feed_dict = fill_feed_dict(data_set, images_placeholder, labels_placeholder)
        true_count += sess.run(eval_correct, feed_dict=feed_dict)
    precision = float(true_count) / num_examples
    print('Num examples: %d  Num correct: %d  Precision @ 1: %0.04f' %
          (num_examples, true_count, precision))

def run_training():
    '''Train MNIST for  a number of steps'''
    # get the sets of images and labels for training  validation and test on MNIST
    data_sets = input_data.read_data_sets(FLAGS.input_data_dir, FLAGS.fake_data)

    # Tell Tensorflow that the model will be built into the default graph
    with tf.Graph().as_default():
        #Generate placeholders for the images and labels
        images_placeholder, labels_placeholder = placeholder_inputs(FLAGS.batch_size)

    # build a graph that computes predictions from the inference model
    logits = mnist.inference(images_placeholder, FLAGS.hidden1, FLAGS.hidden2)

    # add to graph the ops for loss calculation
    loss = mnist.loss(logits, labels_placeholder)

    # add to the graph the ops that calculate and apply gradients
    train_op = mnist.training(loss, FLAGS.learning_rate)

    # add the Op to compare the logits to the labels during evaluation
    eval_correct = mnist.evaluation(logits, labels_placeholder)

    # bulid the summary Tensor based on the TF collection of Summaries
    summary = tf.summary.merge_all()

    # add the variable initializer Op
    init = tf.global_variables_initializer()

    # create a saver fro writing training checkpoint
    saver = tf.train.Saver()

    # create a session for running Ops on the Graph
    sess = tf.compat.v1.Session()

    # Instantiate a SummaryWriter to output summaries and the graph
    summary_writer = tf.summary.FileWriter(FLAGS.log_dir, sess.graph)

    # and then after everything is built

    # run the Op to initialize the variables
    sess.run(init)

    # start the training loop
    for step in range(FLAGS.max_steps):
        start_time = time.time()

        # Fill a feed dictionary with the actual set of images and labels
        # for this particular training step.
        feed_dict = fill_feed_dict(data_sets.train,
                                   images_placeholder,
                                   labels_placeholder)

        # Run one step of the model.  The return values are the activations
        # from the `train_op` (which is discarded) and the `loss` Op.  To
        # inspect the values of your Ops or variables, you may include them
        # in the list passed to sess.run() and the value tensors will be
        # returned in the tuple from the call.
        _, loss_value = sess.run([train_op, loss],
                                 feed_dict=feed_dict)

        duration = time.time() - start_time

        # write the summaries and print an overview fairly often
        if step % 100 == 0:
            # Print status to stdout.
            print('Step %d: loss = %.2f (%.3f sec)' % (step, loss_value, duration))
            # Update the events file.
            summary_str = sess.run(summary, feed_dict=feed_dict)
            summary_writer.add_summary(summary_str, step)
            summary_writer.flush()

        # Save a checkpoint and evaluate the model periodically.
        if (step + 1) % 1000 == 0 or (step + 1) == FLAGS.max_steps:
            checkpoint_file = os.path.join(FLAGS.log_dir, 'model.ckpt')
            saver.save(sess, checkpoint_file, global_step=step)
            # Evaluate against the training set.
            print('Training Data Eval:')
            do_evaluation(sess,
                    eval_correct,
                    images_placeholder,
                    labels_placeholder,
                    data_sets.train)
            # Evaluate against the validation set.
            print('Validation Data Eval:')
            do_evaluation(sess,
                    eval_correct,
                    images_placeholder,
                    labels_placeholder,
                    data_sets.validation)
            # Evaluate against the test set.
            print('Test Data Eval:')
            do_evaluation(sess,
                    eval_correct,
                    images_placeholder,
                    labels_placeholder,
                    data_sets.test)


def main():
    if tf.gfile.Exists(FLAGS.log_dir):
        tf.gfile.DeleteRecursively(FLAGS.log_dir)
    tf.gfile.MakeDirs(FLAGS.log_dir)
    run_training()


def main(_):
  if tf.gfile.Exists(FLAGS.log_dir):
    tf.gfile.DeleteRecursively(FLAGS.log_dir)
  tf.gfile.MakeDirs(FLAGS.log_dir)
  run_training()


if __name__ == '__main__':
  parser = argparse.ArgumentParser()
  parser.add_argument(
      '--learning_rate',
      type=float,
      default=0.01,
      help='Initial learning rate.'
  )
  parser.add_argument(
      '--max_steps',
      type=int,
      default=2000,
      help='Number of steps to run trainer.'
  )
  parser.add_argument(
      '--hidden1',
      type=int,
      default=128,
      help='Number of units in hidden layer 1.'
  )
  parser.add_argument(
      '--hidden2',
      type=int,
      default=32,
      help='Number of units in hidden layer 2.'
  )
  parser.add_argument(
      '--batch_size',
      type=int,
      default=100,
      help='Batch size.  Must divide evenly into the dataset sizes.'
  )
  parser.add_argument(
      '--input_data_dir',
      type=str,
      default=os.path.join(os.getenv('TEST_TMPDIR', ''),
                           'input_data'),
      help='Directory to put the input data.'
  )
  parser.add_argument(
      '--log_dir',
      type=str,
      default=os.path.join(os.getenv('TEST_TMPDIR', ''),
                           'fully_connected_feed'),
      help='Directory to put the log data.'
  )
  parser.add_argument(
      '--fake_data',
      default=False,
      help='If true, uses fake data for unit testing.',
      action='store_true'
  )

  FLAGS, unparsed = parser.parse_known_args()
  tf.app.run(main=main, argv=[sys.argv[0]] + unparsed)

  注意事項:

此處參數注意修改!!

  結果展示:

Extracting data	rain-images-idx3-ubyte.gz
Extracting data	rain-labels-idx1-ubyte.gz
Extracting data	10k-images-idx3-ubyte.gz
Extracting data	10k-labels-idx1-ubyte.gz
Step 0: loss = 2.30 (0.114 sec)
Step 100: loss = 2.11 (0.002 sec)
Step 200: loss = 1.92 (0.002 sec)
Step 300: loss = 1.49 (0.002 sec)
Step 400: loss = 1.22 (0.002 sec)
Step 500: loss = 0.95 (0.003 sec)
Step 600: loss = 0.73 (0.003 sec)
Step 700: loss = 0.58 (0.004 sec)
Step 800: loss = 0.58 (0.002 sec)
Step 900: loss = 0.50 (0.002 sec)
Training Data Eval:
Num examples: 55000  Num correct: 47310  Precision @ 1: 0.8602
Validation Data Eval:
Num examples: 5000  Num correct: 4365  Precision @ 1: 0.8730
Test Data Eval:
Num examples: 10000  Num correct: 8628  Precision @ 1: 0.8628
Step 1000: loss = 0.51 (0.017 sec)
Step 1100: loss = 0.46 (0.104 sec)
Step 1200: loss = 0.50 (0.002 sec)
Step 1300: loss = 0.40 (0.003 sec)
Step 1400: loss = 0.57 (0.003 sec)
Step 1500: loss = 0.40 (0.003 sec)
Step 1600: loss = 0.42 (0.003 sec)
Step 1700: loss = 0.44 (0.003 sec)
Step 1800: loss = 0.41 (0.002 sec)
Step 1900: loss = 0.37 (0.000 sec)
Training Data Eval:
Num examples: 55000  Num correct: 49375  Precision @ 1: 0.8977
Validation Data Eval:
Num examples: 5000  Num correct: 4529  Precision @ 1: 0.9058
Test Data Eval:
Num examples: 10000  Num correct: 8985  Precision @ 1: 0.8985

  準確率比起卷積神經網絡還是差點,但是效果還是不錯的。這里我們主要是學習這個過程,了解深度學習訓練模型的過程,所以還好。

2,部分代碼解析

下載

  在run_training() 方法的一開始, input_data.read_data_sets() 函數會確保我們的本地訓練文件夾中,是否已經下載了正確的數據,然后將這些數據解壓并返回一個含有DataSet實例的字典。

data_sets = input_data.read_data_sets(FLAGS.train_dir, FLAGS.fake_data)

  注意:fake_data標記是用于單元測試的。

  數據展示如下:

輸入與占位符(inputs and placeholders)

  placeholder_inputs() 函數將生成兩個 tf.placeholder 操作,定義傳入圖表中的 shape 參數, shape參數中包括 batch_size 值,后續還會將實際的訓練用例傳入圖表。

images_placeholder = tf.placeholder(tf.float32, shape=(batch_size,
                                                       IMAGE_PIXELS))

labels_placeholder = tf.placeholder(tf.int32, shape=(batch_size))

  在訓練循環(training loop)的后續步驟中,傳入的整個圖像和標簽數據集會被切片,以符合每一個操作所設置的batch_size值,占位符操作將會填補以符合這個batch_size值。然后使用feed_dict參數,將數據傳入sess.run()函數。

構建圖表(Build the Graph)

  在為數據創建占位符之后,就可以運行full_connected_mnist.py文件,經過三階段的模式函數操作: inference() loss() training() 圖表就構建完成了。

inference():盡可能的構建好圖表,滿足促使神經網絡向前反饋并作出預測的要求。
loss():往inference 圖表中添加生成損失(loss)所需要的操作(ops)
training() :往損失函數中添加計算并應用梯度(gradients)所需要的操作

推理(Inference)

  inference()函數會盡可能的構建圖表,做到返回包含了預測結果(output prediction)的Tensor。

  它接受圖像占位符為輸入,在此基礎上借助ReLU(Rectified Linear Units)激活函數,構建一對完全連接層(layers),以及一個有著十個節點(node),指明了輸出 logits 模型的線性層。

  每一層都創建于一個唯一的 tf.name_scope之下,創建于該作用域之下的所有元素都將帶有其前綴。

with tf.name_scope('hidden1') as scope:

  在定義的作用域中,每一層所使用的權重和偏差都在 tf.Variable實例中生成,并且包含了各自期望的shape。

weights = tf.Variable(
    tf.truncated_normal([IMAGE_PIXELS, hidden1_units],
                        stddev=1.0 / math.sqrt(float(IMAGE_PIXELS))),
    name='weights')

biases = tf.Variable(tf.zeros([hidden1_units]),
                     name='biases')

  例如,當這些層是在hidden1 作用域下生成時,賦予權重變量的獨特名稱將會是“hidden1/weights”。

  每個變量在構建時,都會獲得初始化操作(initializer ops)。

  在這種最常見的情況下,通過tf.truncated_normal函數初始化權重變量,給賦予的shape則是一個二維tensor,其中第一個維度代表該層中權重變量所連接(connect from)的單元數量,第二個維度代表該層中權重變量所連接到的(connect to)單元數量。對于名叫hidden1的第一層,相應的維度則是[IMAGE_PIXELS, hidden1_units],因為權重變量將圖像輸入連接到了hidden1層。tf.truncated_normal初始函數將根據所得到的均值和標準差,生成一個隨機分布。

  然后,通過tf.zeros函數初始化偏差變量(biases),確保所有偏差的起始值都是0,而它們的shape則是其在該層中所接到的(connect to)單元數量。

  圖表的三個主要操作,分別是兩個tf.nn.relu操作,它們中嵌入了隱藏層所需的tf.matmul;以及logits模型所需的另外一個tf.matmul。三者依次生成,各自的tf.Variable實例則與輸入占位符或下一層的輸出tensor所連接。

hidden1 = tf.nn.relu(tf.matmul(images, weights) + biases)

hidden2 = tf.nn.relu(tf.matmul(hidden1, weights) + biases)

logits = tf.matmul(hidden2, weights) + biases

  最后,程序會返回包含了輸出結果的logits Tensor。

損失(loss)

  loss()函數通過添加所需要的損失操作,進一步構建圖表。

  首先,labels_placeholder中的值,將被編碼為一個含有1-hot values 的Tensor。例如,如果類標識符為“3”,那么該值就會被轉換為:[0, 0, 0, 1, 0, 0, 0, 0, 0, 0]

batch_size = tf.size(labels)
labels = tf.expand_dims(labels, 1)
indices = tf.expand_dims(tf.range(0, batch_size, 1), 1)
concated = tf.concat(1, [indices, labels])
onehot_labels = tf.sparse_to_dense(
    concated, tf.pack([batch_size, NUM_CLASSES]), 1.0, 0.0)

  之后,又添加一個 tf.nn.softmax_cross_entropy_with_logits操作,用來比較 inference() 函數與 1-hot 標簽所輸出的 logits Tensor。

cross_entropy = tf.nn.softmax_cross_entropy_with_logits(logits,
                                                        onehot_labels,
                                                        name='xentropy')

  然后,使用tf.reduce_mean() 函數,計算 batch 維度(第一維度)下交叉熵(cross entropy)的平均值,將該值作為總損失。

loss = tf.reduce_mean(cross_entropy, name='xentropy_mean')

  最后程序會返回包含了損失值的Tensor。

訓練training()

  training() 函數添加了通過梯度下降(gradient descent)將損失最小化所需要的操作。

  首先,該函數從loss() 函數中獲得損失Tensor,將其交給 tf.scalar_summary,后者在于SummaryWriter配合使用時,可以向事件文件(events file)中生成匯總值(summary values)。每次寫入匯總值時,它都會釋放損失Tensor的當前值(snapshot value)。

tf.scalar_summary(loss.op.name, loss)

  接下來,我們實例化一個tf.train.GradientDescentOptimizer,負責按照所要求的學習效率(learning rate)應用梯度下降法(gradients)。

optimizer = tf.train.GradientDescentOptimizer(FLAGS.learning_rate)

  之后,我們生成一個變量用于保存全局訓練步驟(global training step)的數值,并使用minimize()函數更新系統中的三角權重(triangle weights)、增加全局步驟的操作。根據慣例,這個操作被稱為train_op,是TensorFlow會話(session)誘發一個完整訓練步驟所必須運行的操作(見下文)。

global_step = tf.Variable(0, name='global_step', trainable=False)
train_op = optimizer.minimize(loss, global_step=global_step)

  最后,程序返回包含了訓練操作(training op)輸出結果的Tensor。

圖表

  在 run_training() 這個函數的一開始,是一個python語言的with命令,這個命令表明所有已經構建的操作都要與默認的 tf.Graph 全局實例關聯起來。

with tf.Graph().as_default():

  tf.Graph實例是一系列可以作為整體執行的操作。TensorFlow的大部分場景只需要依賴默認圖表一個實例即可。

會話

  完成全部的構建準備,生成全部所需的操作之后,我們就可以創建一個tf.Session,用于運行圖表。

sess = tf.Session()

  當然,我們也可以利用with 代碼塊生成Session,限制作用域:

with tf.Session() as sess:

  Session函數中沒有傳入參數,表明該代碼將會依附于(如果還沒有創建會話,則會創建新的會話)默認的本地會話。

  生成會話之后,所有tf.Variable實例都會立即通過調用各自初始化操作中的sess.run()函數進行初始化。

init = tf.initialize_all_variables()
sess.run(init)

  sess.run()方法將會運行圖表中與作為參數傳入的操作相對應的完整子集。在初次調用時,init操作只包含了變量初始化程序tf.group。圖表的其他部分不會在這里,而是在下面的訓練循環運行。

訓練循環

  完成會話中變量的初始化之后,就可以開始訓練了。

  訓練的每一步都是通過用戶代碼控制,而能實現有效訓練的最簡單循環就是:

for step in range(max_steps):
    sess.run(train_op)

  但是我們這里需要更復雜一點,因為我們必須把輸入的數據根據每一步的情況進行切分,以匹配之前生成的占位符。

向圖表提供反饋

  執行每一步時,我們的代碼會生成一個反饋字典(feed dictionary),其中包含對應步驟中訓練所要使用的例子,這些例子的哈希鍵就是其所代表的占位符操作。

  fill_feed_dict函數會查詢給定的DataSet,索要下一批次batch_size的圖像和標簽,與占位符相匹配的Tensor則會包含下一批次的圖像和標簽。

images_feed, labels_feed = data_set.next_batch(FLAGS.batch_size)

  然后,以占位符為哈希鍵,創建一個python字典對象,鍵值則是其代表的反饋Tensor。

feed_dict = {
    images_placeholder: images_feed,
    labels_placeholder: labels_feed,
}

  這個字典隨后作為feed_dict參數,傳入sess.run()函數中,為這一步的訓練提供輸入樣例。

檢查狀態

  在運行sess.run函數時,要在代碼中明確其需要獲取的兩個值:[train_op, loss]

for step in range(FLAGS.max_steps):
    feed_dict = fill_feed_dict(data_sets.train,
                               images_placeholder,
                               labels_placeholder)
    _, loss_value = sess.run([train_op, loss],
                             feed_dict=feed_dict)

  因為要獲取這兩個值,sess.run()會返回一個有兩個元素的元組。其中每一個Tensor對象,對應了返回的元組中的numpy數組,而這些數組中包含了當前這步訓練中對應Tensor的值。由于train_op并不會產生輸出,其在返回的元祖中的對應元素就是None,所以會被拋棄。但是,如果模型在訓練中出現偏差,lossTensor的值可能會變成NaN,所以我們要獲取它的值,并記錄下來。

  假設訓練一切正常,沒有出現NaN,訓練循環會每隔100個訓練步驟,就打印一行簡單的狀態文本,告知用戶當前的訓練狀態。

if step % 100 == 0:
    print 'Step %d: loss = %.2f (%.3f sec)' % (step, loss_value, duration)

  

狀態可視化

  為了釋放TensorBoard所使用的事件文件(events file),所有的即時數據(在這里只有一個)都要在圖表構建階段合并至一個操作(op)中。

summary_op = tf.merge_all_summaries()

  在創建好會話(session)之后,可以實例化一個tf.train.SummaryWriter,用于寫入包含了圖表本身和即時數據具體值的事件文件。

summary_writer = tf.train.SummaryWriter(FLAGS.train_dir,
                                        graph_def=sess.graph_def)

  最后,每次運行summary_op時,都會往事件文件中寫入最新的即時數據,函數的輸出會傳入事件文件讀寫器(writer)的add_summary()函數。

summary_str = sess.run(summary_op, feed_dict=feed_dict)
summary_writer.add_summary(summary_str, step)

  

執行sess會話的下面代碼報錯:

    sess = tf.compat.v1.Session()

AttributeError: module 'tensorflow.python.util.compat' has no attribute 'v1'

  解決方法:

    sess = tf.Session()

  

參考文獻:https://blog.csdn.net/GeForce_GTX1080Ti/article/details/80119194

TensorFlow官網(http://www.tensorfly.cn/tfdoc/tutorials/mnist_beginners.html)

http://wiki.jikexueyuan.com/project/tensorflow-zh/tutorials/mnist_tf.html

https://github.com/tensorflow/tensorflow/blob/master/tensorflow/examples/tutorials/mnist/fully_connected_feed.py

此處是做以筆記,記錄學習過程,若有侵權,請聯系我

總結

以上是生活随笔為你收集整理的tensorflow学习笔记——使用TensorFlow操作MNIST数据(1)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

日韩精品免费一区二区 | 欧美精品一区二区在线观看 | 欧美国产日韩中文 | 有码一区二区三区 | 国产一级二级三级视频 | 成人avav | 最新婷婷色 | 国产精品专区在线 | 日韩电影一区二区在线观看 | 亚洲一区精品人人爽人人躁 | 激情网五月婷婷 | 欧美一区二区伦理片 | 91精选在线 | 国产明星视频三级a三级点| 偷拍区另类综合在线 | 狠狠色香婷婷久久亚洲精品 | 99热精品免费观看 | 久久久一本精品99久久精品 | 久草在线视频免费资源观看 | www.日本色| 一区三区视频在线观看 | 在线99视频 | 丁香激情婷婷 | 国产亚洲久一区二区 | 国产你懂的在线 | 99高清视频有精品视频 | 成人蜜桃网 | 韩日av在线 | 国产网红在线观看 | 在线蜜桃视频 | 一级久久精品 | 操操碰| 久久久久久网址 | 天天射天天爱天天干 | 日本资源中文字幕在线 | 在线a亚洲视频播放在线观看 | 日b黄色片| 超级碰碰碰碰 | av免费在线看网站 | 久久综合给合久久狠狠色 | 天天干天天操天天爱 | 狠狠的干狠狠的操 | 91久久黄色 | 亚洲一区天堂 | 蜜臀av性久久久久蜜臀aⅴ涩爱 | 国产精品6999成人免费视频 | 亚洲精品在线视频 | 日韩高清在线一区二区 | 毛片网站在线观看 | 欧美日韩精品电影 | 中文字幕在线观看一区 | 在线黄网站| 久久人网 | 伊人官网| 久久久久久久久久久免费av | 日韩av电影中文字幕 | 色婷婷六月 | 午夜久久福利影院 | 黄色一级动作片 | 一区免费观看 | av福利超碰网站 | 欧美日韩午夜在线 | 狠狠干综合网 | 啪啪免费观看网站 | 午夜国产一区 | av在线一级 | 精品久久久网 | 国产精品久久久久久吹潮天美传媒 | 欧美日韩国产在线精品 | 中文在线字幕免费观看 | 91chinesexxx | 天天av天天 | 西西人体www444 | 色久天| 在线观看国产永久免费视频 | 国产传媒一区在线 | 99热国产在线中文 | 久草免费福利在线观看 | 久久国产精品视频观看 | 久久污视频| 黄色毛片视频免费观看中文 | 免费进去里的视频 | 久久深爱网 | 国产一区欧美二区 | 九草视频在线观看 | 久久精品国产美女 | 久久久国产精品电影 | 狠狠干婷婷色 | 一区二区三区av在线 | 看毛片的网址 | 亚洲精选国产 | 日一日操一操 | 在线精品一区二区 | 日韩在线一区二区免费 | 久草在线视频资源 | 国产精品尤物 | 久久精品一区二区三区视频 | 天天摸天天干天天操天天射 | 免费男女羞羞的视频网站中文字幕 | 久久精品观看 | 国产亚洲视频在线观看 | 97精品国产一二三产区 | 日本激情中文字幕 | 午夜美女福利直播 | 亚洲涩综合| 国产日产亚洲精华av | 丝袜美女视频网站 | 综合天天色 | 欧美天堂视频在线 | 美腿丝袜一区二区三区 | 久草青青在线观看 | 精品国偷自产国产一区 | 亚洲成人精品在线观看 | 五月婷久久 | 天天五月天色 | 午夜免费视频网站 | 久久av影视 | 福利一区二区三区四区 | 97色婷婷人人爽人人 | 欧洲视频一区 | 日本公妇色中文字幕 | 亚洲国产精品成人女人久久 | 久草在线视频资源 | 亚洲片在线资源 | 久久夜夜夜 | 国产精品系列在线观看 | 探花视频在线版播放免费观看 | 国产精品三级视频 | 亚洲三级在线免费观看 | 超碰在线观看99 | 在线一区二区三区 | 最近中文字幕高清字幕免费mv | 黄色毛片视频免费观看中文 | 国产精品久久久久一区二区国产 | 国产精品久久久久av | 国产在线小视频 | 国产黄色大片 | 一区二区免费不卡在线 | 日韩一区二区三区观看 | 69精品在线观看 | 亚洲色视频 | 在线观看韩日电影免费 | 日韩免费在线观看网站 | 成人97视频 | 成人av片在线观看 | 欧美日韩不卡一区二区 | 在线一级片 | 99精品福利 | 在线观看91精品国产网站 | 99热999 | 亚洲香蕉视频 | 91福利视频免费观看 | 欧美老女人xx | 国产一区二区日本 | 一区二区视频在线免费观看 | 在线三级av | av成人在线网站 | 在线国产中文 | 亚洲在线黄色 | 精品一区二区视频 | 九九热免费在线观看 | 久插视频 | 天天射天天色天天干 | 97色在线观看 | 激情六月婷婷久久 | 天天天天爱天天躁 | 亚洲精选视频免费看 | 天天射天天操天天干 | 欧美一级久久 | 久久久91精品国产一区二区精品 | 成人免费观看完整版电影 | 欧美激情视频一区二区三区免费 | 免费观看一级 | 黄色免费高清视频 | 日韩精品免费在线 | 久久久久国产成人免费精品免费 | 日日夜夜天天久久 | 99电影| 五月天亚洲综合小说网 | 国产区免费在线 | 99精品国产福利在线观看免费 | 91视频在线观看下载 | 69绿帽绿奴3pvideos | 国产精品欧美在线 | 精品久久久久久亚洲综合网站 | 天天色天天干天天色 | 精品亚洲成人 | 欧美 日韩 性 | 久久艹综合 | 丁香激情综合久久伊人久久 | 中文字幕免| 依人成人综合网 | 99久久久久成人国产免费 | 色综合天 | 美女国产在线 | 黄色不卡av | 一级片视频免费观看 | 欧美精品乱码久久久久 | 久久精品国产亚洲精品2020 | 免费日韩电影 | 欧美日韩破处 | 国产精品va最新国产精品视频 | 日本公妇在线观看高清 | 91大神在线观看视频 | 免费在线中文字幕 | 狠狠插天天干 | 97干com| 国产h在线播放 | 国产成人精品国内自产拍免费看 | 免费男女羞羞的视频网站中文字幕 | 天天干天天操天天 | 国产美女视频黄a视频免费 久久综合九色欧美综合狠狠 | 亚洲精品在线网站 | 久久久久久久久久亚洲精品 | 国产亚洲精品久久久久久大师 | 国产电影一区二区三区四区 | 天天操狠狠操夜夜操 | 91麻豆精品国产午夜天堂 | 天天操天天舔天天爽 | 亚洲乱码国产乱码精品天美传媒 | 日韩av播放在线 | 久久99网| 久久国产精品免费一区二区三区 | 国产原创在线观看 | 国产成人免费观看 | 久久免费黄色 | 在线观看91精品视频 | 国产一区二区在线影院 | 91av在线免费观看 | 免费在线观看不卡av | 久久毛片网站 | 亚洲欧美日韩精品久久久 | 日韩精品在线免费观看 | 国产日韩视频在线观看 | 国产精品三级视频 | 国产一区久久 | 日韩精品不卡 | 91大神电影 | 亚洲精品在线视频网站 | 亚洲国产免费看 | 人人爱人人爽 | 国产成人福利在线观看 | 午夜电影久久 | 亚洲成人蜜桃 | 久久久亚洲国产精品麻豆综合天堂 | 国产美女精品久久久 | 亚洲免费黄色 | 日日射天天射 | 欧美日韩精品在线观看 | 久久一区国产 | 久久国产精品久久国产精品 | 97国产电影 | 六月天色婷婷 | 最近更新好看的中文字幕 | 亚洲精品乱码久久久久久蜜桃欧美 | 国产 欧美 日本 | 国产精品网站一区二区三区 | 久久综合精品国产一区二区三区 | 日韩在线电影一区二区 | 亚洲日韩中文字幕 | 午夜美女网站 | 在线播放一区二区三区 | 草久草久 | 91精品视频在线看 | 一级一级一片免费 | 日韩三级免费 | 狠狠色丁香婷婷综合最新地址 | 国内视频在线观看 | 99中文字幕| 日韩av在线看 | av免费黄色| 久久怡红院 | 天堂av网在线| 国产精品中文字幕av | 91视频在线国产 | 亚洲精品www久久久久久 | 日本中出在线观看 | 精品一区精品二区高清 | 一级免费片| 国产第一页精品 | 精品久久久久久久久久久院品网 | 久久久久久看片 | 欧美午夜性 | www.少妇| 9在线观看免费高清完整版在线观看明 | 日韩最新在线视频 | 99av在线视频| 日韩欧美国产激情在线播放 | 日韩精品一区二区三区免费观看视频 | 999久久精品 | 欧美日韩中字 | 永久免费精品视频网站 | 亚洲一区精品人人爽人人躁 | 精产嫩模国品一二三区 | 在线电影日韩 | 成人香蕉视频 | 亚洲精品欧洲精品 | 国产一区二区三区在线免费观看 | 在线观看福利网站 | 日韩一区正在播放 | 热re99久久精品国产99热 | 成人丁香花 | 成年人app网址| 久久成人国产精品免费软件 | 久久精久久精 | 日本久久不卡视频 | 91av视频免费观看 | 欧美性色综合网站 | 亚洲不卡av一区二区三区 | 在线国产专区 | 97福利在线观看 | 99精品成人 | 人人草在线视频 | 操操操人人人 | 99九九热只有国产精品 | 久久天天躁夜夜躁狠狠85麻豆 | 欧美日韩国内在线 | 人人搞人人干 | 不卡日韩av | 国产成人精品一区二区三区在线 | 成人免费观看视频网站 | 亚洲自拍偷拍色图 | 黄网站污 | 亚洲毛片在线观看. | 国产精品麻豆视频 | 日日干视频 | 国产 日韩 中文字幕 | 欧美精品一区二区免费 | 国产一区电影在线观看 | 日韩在线视频免费播放 | 久久久久亚洲精品国产 | 免费的成人av | 亚洲婷婷综合色高清在线 | 黄p网站在线观看 | 亚洲视频第一页 | 欧美一级片在线观看视频 | 黄a在线看 | 久草在线资源视频 | 午夜在线看 | 日韩成人黄色 | 成人国产精品一区二区 | 久久久国产一区二区三区 | 色婷婷视频在线观看 | 丝袜美女视频网站 | 成人久久国产 | 日韩欧美视频在线播放 | 亚洲va韩国va欧美va精四季 | 手机在线看片日韩 | 麻豆影视在线播放 | 黄色免费观看 | 午夜精品成人一区二区三区 | 99视频免费| av免费线看 | 992tv人人草| 成人黄色在线观看视频 | av资源在线看 | 免费色视频网址 | 亚洲精品高清视频在线观看 | 三级av小说 | 亚洲欧美经典 | 国产精品电影一区 | 久热久草| 久久视频精品在线观看 | 亚洲一区二区精品 | 日韩中文字幕免费 | 91大片网站| 国产精品网站一区二区三区 | 亚洲三级在线免费观看 | 在线视频 日韩 | 日本女人在线观看 | 美女久久一区 | 久久久精品国产免费观看同学 | 成人免费在线看片 | 亚洲成人欧美 | 欧美成人精品欧美一级乱 | 亚洲va天堂va欧美ⅴa在线 | 一区二区三区国产精品 | 亚洲精品视频在线 | 91福利试看 | www.久久精品视频 | 中文字幕一区二区三区四区视频 | 国产精品系列在线 | 亚洲精品在线视频观看 | 日韩在线欧美在线 | 伊人色综合网 | 欧美一级片免费 | 日韩中文字幕在线观看 | 黄网站色视频免费观看 | 免费麻豆| 成人免费毛片aaaaaa片 | 欧美人交a欧美精品 | 四虎在线观看精品视频 | 在线视频电影 | 亚洲v欧美v国产v在线观看 | 欧美久久久久久久久久 | 丁香六月婷 | 国产精品18久久久 | 在线免费观看的av | 国产伦理久久 | 99久久精品免费一区 | 婷婷亚洲五月 | 在线精品亚洲 | 亚洲精品毛片一级91精品 | 91在线资源 | 97国产大学生情侣白嫩酒店 | 精品伊人久久久 | 中文字幕欧美日韩va免费视频 | 久久精品一区 | 欧美小视频在线观看 | 亚洲黄色小说网址 | 久久久99久久 | 婷婷久久国产 | 亚洲一区不卡视频 | 深爱激情综合 | 日韩av片无码一区二区不卡电影 | 久艹视频在线观看 | 亚洲欧美综合精品久久成人 | 黄污视频网站大全 | 91丨精品丨蝌蚪丨白丝jk | 99视频在线观看免费 | 日本激情动作片免费看 | 久久精品亚洲精品国产欧美 | 超碰在线观看99 | 麻豆国产精品视频 | 亚洲精品一区二区网址 | 日韩在线观看影院 | 蜜臀久久99精品久久久久久网站 | 国产精品一区二区三区免费看 | 91精品国产自产91精品 | 日韩精品久久久久久久电影竹菊 | 综合精品久久 | 青青河边草免费观看 | 国产a国产a国产a | 日韩色av色资源 | 亚洲视频在线看 | 国产成人精品久久 | 国内精品中文字幕 | 人人玩人人添人人 | 欧美日韩一区二区在线 | 女人高潮一级片 | 欧美日韩伦理一区 | 91精品免费在线观看 | 激情www | 成人av影视在线 | 免费欧美高清视频 | 波多野结衣久久资源 | 一本一本久久a久久精品综合 | 超碰在线人 | 97成人精品 | 人人天天夜夜 | 中文字幕在线第一页 | 免费观看性生活大片3 | 五月婷婷久 | 免费黄色在线 | 久久精品电影院 | 日本午夜在线亚洲.国产 | 日韩美女av在线 | 99精品视频免费在线观看 | 麻豆国产精品永久免费视频 | 麻豆免费在线视频 | 热re99久久精品国产66热 | 色黄久久久久久 | 久久精品亚洲一区二区三区观看模式 | 欧美调教网站 | 最近中文字幕免费av | 国产成人在线看 | 亚洲国产无| 黄色av电影在线观看 | 日韩av在线网站 | 中文字幕一区二区三 | 久草av在线播放 | 久久这里只有精品首页 | 国产一区二区在线免费视频 | 99精品国产福利在线观看免费 | 久久精品免费播放 | 久久午夜色播影院免费高清 | 96av在线视频 | 天天干,夜夜爽 | 五月天综合色 | 国产麻豆剧传媒免费观看 | 精品免费久久久久久 | av中文字幕在线看 | 国产成人一区二区三区电影 | 成人在线免费视频 | 日韩视频一区二区 | 中文字幕乱码一区二区 | 中文字幕亚洲字幕 | 日韩在线免费观看视频 | 亚洲乱亚洲乱亚洲 | 色中色亚洲| 国产亚洲精品久久19p | 美女国内精品自产拍在线播放 | 美女在线国产 | 国产中文伊人 | 亚洲最大成人免费网站 | 国产偷国产偷亚洲清高 | 美女免费黄网站 | 狠狠色丁香婷婷综合最新地址 | 成人一区二区在线观看 | 亚洲精品视频在线观看免费视频 | 国产一级特黄电影 | 99久久久国产精品美女 | 免费成人结看片 | 国产精选视频 | 久久久久久久国产精品影院 | 成在人线av | 色婷婷综合久久久 | 亚洲精品乱码久久久久久写真 | 久久一久久 | 免费大片av | 日韩精品一区二区三区中文字幕 | 青草视频在线看 | 最新日韩在线 | 国产欧美日韩视频 | 伊人五月天综合 | 免费观看一级一片 | 天堂视频中文在线 | 性色av免费观看 | 久久久久国产一区二区 | 久久在线免费观看 | 久草电影在线观看 | 久久乐九色婷婷综合色狠狠182 | 国产黑丝袜在线 | 国产成人精品在线观看 | 天天操天天谢 | 日批网站在线观看 | 久久久2o19精品 | 国产91九色蝌蚪 | 特级a老妇做爰全过程 | 欧美性生活免费看 | 不卡的av电影在线观看 | 中文字幕免费播放 | 91精品国产高清 | 在线观看一区视频 | 亚洲一区二区视频在线 | 8x成人免费视频 | 狠狠狠狠狠狠狠狠干 | 999超碰| 国产亚洲精品久久久久久久久久久久 | а天堂中文最新一区二区三区 | 日韩av免费大片 | 在线国产视频一区 | 国产精品麻豆免费版 | 国产亚州av | 色诱亚洲精品久久久久久 | 午夜视频在线观看欧美 | 国产精品乱码一区二三区 | 9在线观看免费高清完整版在线观看明 | 天天鲁一鲁摸一摸爽一爽 | 亚洲精品在线观看的 | 免费情趣视频 | 久久免视频 | 在线 国产一区 | 三级黄免费看 | 91在线免费观看国产 | 激情婷婷色 | 9999毛片| 韩国av电影在线观看 | 久久成人国产精品入口 | 日日夜夜精品免费视频 | 国产福利免费在线观看 | 97福利在线观看 | 欧美精品视 | 国产二级视频 | 色婷婷激情五月 | 成片视频在线观看 | 91麻豆精品国产91久久久久久 | 日韩中文字幕免费在线观看 | 国产精品九九热 | 日韩成人看片 | 69国产精品视频 | 国产欧美综合在线观看 | 日韩中字在线观看 | 亚洲综合在线五月 | 69精品人人人人 | av三级av| 亚洲精品视频观看 | 精品一区二区在线免费观看 | 不卡的av电影在线观看 | av电影在线观看完整版一区二区 | 国产精品久久伊人 | 国产精品你懂的在线观看 | 国内久久视频 | 日韩中文字幕免费视频 | 亚洲码国产日韩欧美高潮在线播放 | 欧美激情视频在线观看免费 | 日日草av| 美女精品网站 | 六月色播| 天天干天天射天天操 | 手机在线永久免费观看av片 | 免费看色的网站 | 免费福利片2019潦草影视午夜 | 国产视频在线免费 | 性色视频在线 | a精品视频| 久久久亚洲网站 | av网站手机在线观看 | 欧美激情在线看 | 在线观看黄色小视频 | 久久久久国产视频 | 一级性视频 | 免费一级特黄录像 | 亚洲精品久久久久中文字幕m男 | 欧美成天堂网地址 | 美女视频网站久久 | 日韩系列在线观看 | 国产精品永久 | 人人揉人人揉人人揉人人揉97 | 成年人免费av网站 | 日韩视频二区 | 国产99久久精品一区二区永久免费 | 99久久精品国产一区二区成人 | 亚州中文av | 丝袜美腿亚洲综合 | 国产欧美三级 | h文在线观看免费 | 四虎精品成人免费网站 | 中文字幕免费观看 | 国产免费中文字幕 | 丁香九月婷婷综合 | 97夜夜澡人人双人人人喊 | 国产成人久久av免费高清密臂 | 国产高清在线免费观看 | 国产麻豆精品久久一二三 | 成人av电影免费观看 | 久久久久免费电影 | 亚洲日本韩国一区二区 | 激情五月综合 | 亚洲第一香蕉视频 | 午夜精品视频一区 | 黄色小视频在线观看免费 | 国产女人18毛片水真多18精品 | 中文字幕高清免费日韩视频在线 | 色五月成人 | 久久综合九色综合久99 | 久99精品 | 免费看片网址 | 五月婷婷色丁香 | 国产精品麻豆果冻传媒在线播放 | 欧美日韩国产精品久久 | 成人在线视频论坛 | 日本黄色免费在线 | 丁香5月婷婷 | 久久精品视频一 | 日韩,精品电影 | 欧美精品在线免费 | 成人全视频免费观看在线看 | 玖玖玖国产精品 | 超碰人人草人人 | 国产黄色av影视 | 亚洲国产中文在线观看 | 国产最新91 | 狠狠色丁香婷婷综合视频 | 久久国内精品99久久6app | 免费看成人a | 久 久久影院 | 成人a毛片| 青青河边草观看完整版高清 | 成人免费视频网站在线观看 | 色多多视频在线观看 | 在线观看日韩精品 | 97高清视频 | 欧美亚洲久久 | 99精品国产高清在线观看 | 欧美专区亚洲专区 | 久久精品国产精品亚洲 | 98涩涩国产露脸精品国产网 | 久久久免费毛片 | 韩国精品视频在线观看 | 日日爱影视 | 毛片永久新网址首页 | 日韩精品第1页 | 久久久96| 成人一区二区三区在线 | 人人爽人人av | 久香蕉 | 91九色蝌蚪国产 | 91网站免费观看 | 免费在线观看午夜视频 | 91麻豆精品国产91久久久久久 | 在线网站黄 | 天堂av网址 | 91亚洲精 | 日韩久久一区二区 | 91黄色小视频 | 久久超碰97| 久草网站 | 久久精品中文字幕免费mv | 日韩.com| 天天透天天插 | 国产精品女同一区二区三区久久夜 | 99电影456麻豆 | 狠狠色丁香婷婷综合视频 | 91免费国产在线观看 | 最新av网址在线观看 | 乱子伦av| 久艹在线播放 | 国内久久看| 97成人资源 | 最近日本中文字幕 | 欧美久久九九 | 日批视频在线观看免费 | 日韩黄色软件 | 亚洲午夜精品一区二区三区电影院 | 久久这里只精品 | 999成人 | 操久久网| 国产一二区视频 | 天天躁天天躁天天躁婷 | 欧美精品中文在线免费观看 | 综合久久五月天 | 国产一级在线观看视频 | 99久久精品午夜一区二区小说 | 麻豆国产视频 | 91在线资源 | 天天插天天干 | 国产二区免费视频 | 91手机电影 | 91亚洲精品久久久蜜桃 | 日韩视频免费观看高清完整版在线 | 欧美激情va永久在线播放 | av免费看在线 | 久久影视一区二区 | 色视频在线免费观看 | 天天射天天射天天 | 日韩在线精品一区 | 四虎在线影视 | 三级黄色a| 久草免费在线观看 | 久久国语 | 日韩免费高清在线 | 国产成人综 | 深爱五月网| 国产成人精品一区二三区 | 黄色视屏在线免费观看 | 91在线小视频 | 欧美 亚洲 另类 激情 另类 | 午夜av在线电影 | 国产精品淫片 | 婷婷九九| 欧美日韩不卡一区 | 在线你懂| 在线观看av麻豆 | 精品毛片在线 | 美女视频黄免费 | 国产精品va在线观看入 | 福利区在线观看 | 九九视频在线播放 | 国内精品视频在线 | 中文字幕中文中文字幕 | 少妇bbw搡bbbb搡bbbb| 中文字幕4 | 一级性视频| 亚洲精品高清视频在线观看 | 中文字幕亚洲欧美 | 国产性天天综合网 | 成人av网址大全 | 青青草国产免费 | 美女亚洲精品 | 久久 精品一区 | 亚洲精品国产精品国自产观看浪潮 | 福利电影久久 | 中午字幕在线 | 69国产盗摄一区二区三区五区 | 久久国产视频网站 | 日本成人a | 日韩中文字幕91 | 欧美日本高清视频 | 日日躁夜夜躁aaaaxxxx | 91最新网址在线观看 | 国产精品视频999 | 一级免费看视频 | 1000部18岁以下禁看视频 | 色资源网免费观看视频 | 久久综合五月天婷婷伊人 | 欧美日bb | 婷婷激情影院 | 亚洲人视频在线 | 在线观看亚洲免费视频 | 精品视频免费在线 | 成人免费看视频 | 亚洲深夜影院 | 日韩成人高清在线 | 免费日韩av电影 | 99在线视频播放 | 91视频高清免费 | 日韩午夜在线播放 | 国内免费久久久久久久久久久 | 久久久久久99精品 | 国产日韩欧美在线一区 | 国产一区免费在线观看 | 亚洲精品福利视频 | www.99热精品| 深爱激情综合网 | 丁香婷婷综合色啪 | ,午夜性刺激免费看视频 | 午夜视频在线网站 | 婷婷激情在线观看 | 五月婷婷综合激情网 | 最近中文字幕国语免费av | 久久久一本精品99久久精品 | 色网站国产精品 | 三上悠亚在线免费 | 亚洲国产午夜精品 | 五月婷婷深开心 | 国产在线理论片 | 国产精品乱码一区二三区 | 日本精品一二区 | 久久国产三级 | 国产一级在线免费观看 | 伊人电影天堂 | 日韩欧美在线综合网 | 97超碰网 | 色综合天天狠天天透天天伊人 | 久久综合操 | av中文在线 | 成人黄色免费观看 | 精品免费一区 | av在线之家电影网站 | 欧美日韩高清一区二区 | 国产精品 欧美 日韩 | 色就是色综合 | 国产精品日韩欧美 | 在线观看深夜福利 | 国产69精品久久app免费版 | 麻豆94tv免费版 | 97视频免费在线观看 | 天天草综合网 | 欧美在线不卡一区 | 亚洲精品国产精品国自产观看 | 日日爱网站| 人人看黄色 | 免费不卡中文字幕视频 | 亚洲va欧美va人人爽 | 久久精品成人 | 免费观看十分钟 | 久久精品99国产 | 欧美一级视频免费 | 国产一区二区不卡在线 | 久久国产成人午夜av影院潦草 | 久久亚洲影视 | 亚洲成人资源在线观看 | 日韩av成人免费看 | 国产在线第三页 | 亚洲欧洲在线视频 | 综合色在线 | 久久在线播放 | 最新黄色av网址 | 国产手机免费视频 | 国产精品黄 | 香蕉视频在线看 | 伊人永久在线 | 精品国产乱码久久久久 | a资源在线| 97视频免费观看 | 高清av免费一区中文字幕 | 丁香国产视频 | 在线导航福利 | 美女网站视频一区 | 色婷婷激情四射 | 91爱爱免费观看 | 成人a在线观看 | 亚洲精品国产欧美在线观看 | 成年人免费av网站 | 欧美精品在线视频观看 | 91视频在线观看免费 | 四虎永久免费网站 | 国内成人av| 欧美激情h| 91中文视频| 最新影院 | 伊人狠狠色丁香婷婷综合 | 午夜久操 | 亚洲精品美女久久久久网站 | 国产精品 中文字幕 亚洲 欧美 | 精品国产伦一区二区三区观看说明 | 国内外成人在线视频 | 四虎影视成人永久免费观看亚洲欧美 | 国产精品99久久久久久人免费 | 日本公乱妇视频 | 亚洲乱码在线观看 | 欧美综合干 | 国产午夜精品福利视频 | 99视频播放| 亚洲欧美视频 | 最近最新最好看中文视频 | 亚洲精品视频在线免费 | 国产精品久久久 | 伊甸园av在线 | 日韩高清在线一区 | 婷婷狠狠操| av在线影片 | 夜夜嗨av色一区二区不卡 | 韩国av一区二区三区在线观看 | 国内综合精品午夜久久资源 | 亚州av网站| 欧美性色综合网站 | 亚洲色图美腿丝袜 | 国产裸体视频网站 | 91人人干 | 久久九九免费视频 | 国产字幕在线播放 | 久久久久久伊人 | 免费亚洲视频 | 中文字幕在线观看免费观看 | 成人动漫一区二区 | 99精品国产高清在线观看 | 韩国在线视频一区 | 日韩精品视频免费看 | 2019中文最近的2019中文在线 | 欧美 日韩 国产 中文字幕 | 亚州国产精品久久久 | 午夜久久久久久久久 | 国产视频丨精品|在线观看 国产精品久久久久久久久久久久午夜 | 久久av中文字幕片 | 精品一区二区免费视频 | 99九九99九九九视频精品 | 黄a在线观看 | 久久久国产在线视频 | 一区二区精品久久 | av在线中文 | 国产精品久久久久婷婷 | 久久久精品一区二区三区 | 久久午夜电影网 | 99国产在线 | 久久国产一二区 | 久久九精品 | 免费日韩一区二区三区 | 天天操天操| 在线免费观看国产 | 狠狠干美女 | 久久精品视频国产 | 97超碰在线人人 | 欧美夫妻性生活电影 | 黄色性av | 91片黄在线观看动漫 | 欧美xxxxx在线视频 | 日韩久久精品一区二区 | 99色人 | 黄色小网站免费看 | 久久这里精品视频 | av中文电影 | 国产麻豆精品久久一二三 | 美女精品久久久 | 日韩视频在线观看视频 | 亚洲精品一区二区三区在线观看 | av中文电影 | 亚洲无吗视频在线 | 久久超级碰| 六月丁香久久 | 超碰在线个人 | 成人动态视频 | 国产精品麻豆果冻传媒在线播放 | 97超碰人人模人人人爽人人爱 | 久久综合国产伦精品免费 | 日韩精品国产一区 | 久久久久福利视频 | 久久一区二区免费视频 | 国产精品久久久久久爽爽爽 | 欧美最猛性xxx| 国产一级做a爱片久久毛片a | 日狠狠| 狠狠操综合 | 99免费在线视频观看 | 中文字幕影视 | 久操操| 久久久久久毛片精品免费不卡 | 四虎国产精品免费观看视频优播 | 国内外成人在线 | 国产一区国产二区在线观看 | 精品理论片 | 久久久久久免费毛片精品 | 国产精品综合在线观看 | 久草久热 | 日韩网站免费观看 | 日韩在线视频在线观看 | 久久精品免费看 | 婷婷激情网站 | 9992tv成人免费看片 | 日韩在线观看网站 | 久草视频99| 日韩欧美专区 | 黄色午夜 | 国产精品高潮呻吟久久久久 | 九九精品在线观看 | av再线观看| 91看成人 | 黄色精品一区二区 | 三级av在线播放 | 中文乱码视频在线观看 | 久久久高清视频 | 白丝av在线 | 又黄又刺激的网站 | 91女神的呻吟细腰翘臀美女 | 日本午夜在线亚洲.国产 | 麻豆你懂的 | 欧美日韩中字 |