tensorflow捲積神經網路-【老魚學tensorflow】

来源:https://www.cnblogs.com/dreampursuer/archive/2018/02/24/8038609.html
-Advertisement-
Play Games

前面我們曾有篇文章中提到過關於用tensorflow訓練手寫28 28像素點的數字的識別,在那篇文章中我們把手寫數字圖像直接碾壓成了一個784列的數據進行識別,但實際上,這個圖像是28 28長寬結構的,我們這次使用CNN捲積神經網路來進行識別。 捲積神經網路我的理解是部分模仿了人眼的功能。 我們在看 ...


前面我們曾有篇文章中提到過關於用tensorflow訓練手寫2828像素點的數字的識別,在那篇文章中我們把手寫數字圖像直接碾壓成了一個784列的數據進行識別,但實際上,這個圖像是2828長寬結構的,我們這次使用CNN捲積神經網路來進行識別。

捲積神經網路我的理解是部分模仿了人眼的功能。
我們在看一個圖像時不是一個像素點一個像素點去分辨的,我們的眼睛天然地具有大局觀,我們看到某個圖像時自動地會把其中的細節部分給聚合起來進行識別,相反,如果我們用個放大鏡看到其中的各個像素點時反而不知道這是啥東西了。

因此捲積神經網路就把每個像素點的圖像進行一定程度上的模糊化,而怎麼進行模糊化呢?它是通過選擇一小片的區域範圍,把這小片中的圖像數據縮小其長寬,但增加其高度值。然後進行某種計算,最終達到有點類似模糊化圖像的目的,但這個模糊化的圖像中反而能夠比較容易識別出相應的邊界及形狀。

具體大家可以到網上搜索相關的理論知識,這裡不細講,只專註於如何在tensorflow中實現 CNN的功能。

之前在tensorflow分類-【老魚學tensorflow】中已經用一般的神經網路進行過手寫數字的識別,我們在那個程式的基礎上來進行,那篇文章的地址為:http://www.cnblogs.com/dreampursuer/p/8026866.html

import tensorflow as tf

# 準備數據
from tensorflow.examples.tutorials.mnist import input_data
mnist = input_data.read_data_sets('D:/todel/python/MNIST_data/', one_hot=True)

def add_layer(inputs, in_size, out_size, activation_function=None):
    """
    添加層
    :param inputs: 輸入數據
    :param in_size: 輸入數據的列數
    :param out_size: 輸出數據的列數
    :param activation_function: 激勵函數
    :return:
    """

    # 定義權重,初始時使用隨機變數,可以簡單理解為在進行梯度下降時的隨機初始點,這個隨機初始點要比0值好,因為如果是0值的話,反覆計算就一直是固定在0中,導致可能下降不到其它位置去。
    Weights = tf.Variable(tf.random_normal([in_size, out_size]))
    # 偏置shape為1行out_size列
    biases = tf.Variable(tf.zeros([1, out_size]) + 0.1)
    # 建立神經網路線性公式:inputs * Weights + biases,我們大腦中的神經元的傳遞基本上也是類似這樣的線性公式,這裡的權重就是每個神經元傳遞某信號的強弱繫數,偏置值是指這個神經元的原先所擁有的電位高低值
    Wx_plus_b = tf.matmul(inputs, Weights) + biases
    if activation_function is None:
        # 如果沒有設置激活函數,則直接就把當前信號原封不動地傳遞出去
        outputs = Wx_plus_b
    else:
        # 如果設置了激活函數,則會由此激活函數來對信號進行傳遞或抑制
        outputs = activation_function(Wx_plus_b)
    return outputs

# 定義輸入數據
xs = tf.placeholder(tf.float32, [None, 28*28])
ys = tf.placeholder(tf.float32, [None, 10]) #10列,就是那個one hot結構的數據

# 定義層,輸入為xs,其有28*28列,輸出為10列one hot結構的數據,激勵函數為softmax,對於one hot類型的數據,一般激勵函數就使用softmax
prediction = add_layer(xs, 28*28, 10, activation_function=tf.nn.softmax)

# 定義loss值
cross_entropy = tf.reduce_mean(-tf.reduce_sum(ys * tf.log(prediction), axis=1))
train_step = tf.train.GradientDescentOptimizer(0.5).minimize(cross_entropy)

sess = tf.Session()
init = tf.global_variables_initializer()
sess.run(init)


def computer_accuracy(v_xs, v_ys):
    """
    計算準確度
    :param v_xs:
    :param v_ys:
    :return:
    """
    # predication是從外部獲得的變數
    global prediction
    # 根據小批量輸入的值計算預測值
    y_pre = sess.run(prediction, feed_dict={xs:v_xs})
    correct_prediction = tf.equal(tf.argmax(y_pre, 1), tf.argmax(v_ys, 1))
    accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
    result = sess.run(accuracy, feed_dict={xs:v_xs, ys:v_ys})
    return result

for i in range(1000):
    batch_xs, batch_ys = mnist.train.next_batch(100)
    sess.run(train_step, feed_dict={xs: batch_xs, ys: batch_ys})
    if i % 50 == 0:
        # 每隔50條列印一下預測的準確率
        print(computer_accuracy(mnist.test.images, mnist.test.labels))

添加必要的函數

生成權重變數

# 生成權重變數
def weight_variable(shape):
    # 產生一個隨機變數
    init = tf.truncated_normal(shape, stddev=0.1)
    return tf.Variable(init)

定義bias變數

# 定義bias變數
def bias_variable(shape):
    # bias的初始值比權重值稍微簡單點,直接用非0常量定義就可以
    init = tf.constant(0.1, shape=shape)
    return tf.Variable(init)

定義捲積神經網路層

# 定義捲積神經網路層
def conv2d(x, W):
    # strides:結構為[1, x方向上的步長,y方向上的步長, 1],這裡x方向上的步長和y方向上的步長都設置為1
    # padding可選值有VALID和SAME,VALID方式會在邊界處比原始圖片小一點,而SAME方式會在邊界處用0來補充,從而保持跟原始圖相同的大小
    return tf.nn.conv2d(x, W, strides=[1, 1, 1, 1], padding='SAME')

定義pooling

# 為了防止跨步太大丟失掉信息,我們會在中間建立一個pooling,使其跨度減小,但在pooling時跨度可以變大一點,這樣在最後的圖片生成時可以把大小減小下來但同時又儘可能保存了相關的信息
def max_pool_2x2(x):
    # strides結構依然為:[1, x方向上的步長,y方向上的步長, 1],這裡x方向上的步長和y方向上的步長都設置為2,這樣在pool時把圖像的大小給減小了
    return tf.nn.max_pool(x, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME')

編寫主程式部分

定義輸入數據

# 定義輸入數據
xs = tf.placeholder(tf.float32, [None, 28*28])
ys = tf.placeholder(tf.float32, [None, 10]) #10列,就是那個one hot結構的數據
keep_prob = tf.placeholder(tf.float32)
# 為了使用捲積神經網路,我們需要把原始的一維的數據變換成長寬表示的平面圖的數據,把xs的形狀變成[-1,28,28,1],-1代表先不考慮輸入的圖片例子多少這個維度,
# 後面的1是channel的數量,因為我們輸入的圖片是黑白的,因此channel是1,例如如果是RGB圖像,那麼channel就是3。
x_image = tf.reshape(xs, [-1, 28, 28, 1])

定義捲積層1

# 定義捲積層1,以5*5的面積進行掃描,因為黑白圖片channel是1所以輸入是1,輸出是32個高度的值
W_conv1 = weight_variable([5, 5, 1, 32])
# bias的大小是32個長度,因此我們傳入它的shape為[32]
b_conv1 = bias_variable([32])
# 定義好了Weight和bias,我們就可以定義捲積神經網路的第一個捲積層h_conv1=conv2d(x_image,W_conv1)+b_conv1,同時我們對h_conv1進行非線性處理,
# 也就是激活函數來處理嘍,這裡我們用的是tf.nn.relu(修正線性單元)來處理,要註意的是,因為採用了SAME的padding方式,輸出圖片的大小沒有變化依然是28x28,
# 只是厚度變厚了,因此現在的輸出大小就變成了28x28x32
h_conv1 = tf.nn.relu(conv2d(x_image, W_conv1) + b_conv1)
# 最後我們再進行pooling的處理就ok啦,經過pooling的處理,輸出大小就變為了14x14x32
h_pool1 = max_pool_2x2(h_conv1)

定義捲積層2

# 定義捲積層2
# 掃描的面積還是定義成5*5,輸入大小為32,因為捲積層1中輸出為32就被設置為這裡的輸入大小了。輸出大小設定為64,也就是變得更高了
W_conv2 = weight_variable([5, 5, 32, 64])
# bias的大小是64個長度,因此我們傳入它的shape為[64]
b_conv2 = bias_variable([64])
# 定義好了Weight和bias,我們就可以定義捲積神經網路的第二個捲積層h_conv2=conv2d(h_pool1,W_conv2)+b_conv2,同時我們對h_conv2進行非線性處理,
# 也就是激活函數來處理嘍,這裡我們用的是tf.nn.relu(修正線性單元)來處理,要註意的是,因為採用了SAME的padding方式,輸出圖片的大小沒有變化依然是在第一層捲集層輸出時的14*14,
# 只是厚度變厚了,因此現在的輸出大小就變成了14x14x64
h_conv2 = tf.nn.relu(conv2d(h_pool1, W_conv2) + b_conv2)
# 最後我們再進行pooling的處理就ok啦,經過pooling的處理,輸出大小就變為了7x7x64
h_pool2 = max_pool_2x2(h_conv2)

定義神經網路全連接層1

# 定義神經網路全連接層1
# 其形狀為h_pool2的輸出形狀7*7*64,輸出為1024個神經元
W_fc1 = weight_variable([7*7*64, 1024])
b_fc1 = bias_variable([1024])
# 把h_pool2的輸出的包含長寬平面的信息形狀轉換成一個維度的數據,相當於變平的操作:[n_samples, 7, 7, 64] => [n_samples, 7*7*64]
h_pool1_flat = tf.reshape(h_pool2, [-1, 7*7*64])
h_fc1 = tf.nn.relu(tf.matmul(h_pool1_flat, W_fc1) + b_fc1)
h_fc1_drop = tf.nn.dropout(h_fc1, keep_prob)

定義神經網路全連接層2

# 定義神經網路全連接層2
# 其輸入為全連接層1的輸出1024,輸出為0-9數字的one hot格式,因此為10列
W_fc2 = weight_variable([1024, 10])
b_fc2 = bias_variable([10])
prediction = tf.nn.softmax(tf.matmul(h_fc1_drop, W_fc2) + b_fc2)

定義loss值

# 定義loss值
cross_entropy = tf.reduce_mean(-tf.reduce_sum(ys * tf.log(prediction), axis=1))
# 對於比較龐大的系統可以用AdamOptimizer比較好一點
train_step = tf.train.AdamOptimizer(1e-4).minimize(cross_entropy)
# train_step = tf.train.GradientDescentOptimizer(0.5).minimize(cross_entropy)

執行

sess = tf.Session()
init = tf.global_variables_initializer()
sess.run(init)

for i in range(1000):
    batch_xs, batch_ys = mnist.train.next_batch(100)
    sess.run(train_step, feed_dict={xs: batch_xs, ys: batch_ys, keep_prob: 0.5})
    if i % 50 == 0:
        # 每隔50條列印一下預測的準確率
        print(computer_accuracy(mnist.test.images, mnist.test.labels))

全部代碼

import tensorflow as tf

def add_layer(inputs, in_size, out_size, activation_function=None):
    """
    添加層
    :param inputs: 輸入數據
    :param in_size: 輸入數據的列數
    :param out_size: 輸出數據的列數
    :param activation_function: 激勵函數
    :return:
    """

    # 定義權重,初始時使用隨機變數,可以簡單理解為在進行梯度下降時的隨機初始點,這個隨機初始點要比0值好,因為如果是0值的話,反覆計算就一直是固定在0中,導致可能下降不到其它位置去。
    Weights = tf.Variable(tf.random_normal([in_size, out_size]))
    # 偏置shape為1行out_size列
    biases = tf.Variable(tf.zeros([1, out_size]) + 0.1)
    # 建立神經網路線性公式:inputs * Weights + biases,我們大腦中的神經元的傳遞基本上也是類似這樣的線性公式,這裡的權重就是每個神經元傳遞某信號的強弱繫數,偏置值是指這個神經元的原先所擁有的電位高低值
    Wx_plus_b = tf.matmul(inputs, Weights) + biases
    if activation_function is None:
        # 如果沒有設置激活函數,則直接就把當前信號原封不動地傳遞出去
        outputs = Wx_plus_b
    else:
        # 如果設置了激活函數,則會由此激活函數來對信號進行傳遞或抑制
        outputs = activation_function(Wx_plus_b)
    return outputs

def computer_accuracy(v_xs, v_ys):
    """
    計算準確度
    :param v_xs:
    :param v_ys:
    :return:
    """
    # predication是從外部獲得的變數
    global prediction
    # 根據小批量輸入的值計算預測值
    y_pre = sess.run(prediction, feed_dict={xs:v_xs, keep_prob: 1})
    correct_prediction = tf.equal(tf.argmax(y_pre, 1), tf.argmax(v_ys, 1))
    accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
    result = sess.run(accuracy, feed_dict={xs:v_xs, ys:v_ys, keep_prob: 1})
    return result


# 生成權重變數
def weight_variable(shape):
    # 產生一個隨機變數
    init = tf.truncated_normal(shape, stddev=0.1)
    return tf.Variable(init)

# 定義bias變數
def bias_variable(shape):
    # bias的初始值比權重值稍微簡單點,直接用非0常量定義就可以
    init = tf.constant(0.1, shape=shape)
    return tf.Variable(init)

# 定義捲積神經網路層
def conv2d(x, W):
    # strides:結構為[1, x方向上的步長,y方向上的步長, 1],這裡x方向上的步長和y方向上的步長都設置為1
    # padding可選值有VALID和SAME,VALID方式會在邊界處比原始圖片小一點,而SAME方式會在邊界處用0來補充,從而保持跟原始圖相同的大小
    return tf.nn.conv2d(x, W, strides=[1, 1, 1, 1], padding='SAME')

# 為了防止跨步太大丟失掉信息,我們會在中間建立一個pooling,使其跨度減小,但在pooling時跨度可以變大一點,這樣在最後的圖片生成時可以把大小減小下來但同時又儘可能保存了相關的信息
def max_pool_2x2(x):
    # strides結構依然為:[1, x方向上的步長,y方向上的步長, 1],這裡x方向上的步長和y方向上的步長都設置為2,這樣在pool時把圖像的大小給減小了
    return tf.nn.max_pool(x, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME')


# 準備數據
from tensorflow.examples.tutorials.mnist import input_data
mnist = input_data.read_data_sets('D:/todel/python/MNIST_data/', one_hot=True)


# 定義輸入數據
xs = tf.placeholder(tf.float32, [None, 28*28])
ys = tf.placeholder(tf.float32, [None, 10]) #10列,就是那個one hot結構的數據
keep_prob = tf.placeholder(tf.float32)
# 為了使用捲積神經網路,我們需要把原始的一維的數據變換成長寬表示的平面圖的數據,把xs的形狀變成[-1,28,28,1],-1代表先不考慮輸入的圖片例子多少這個維度,
# 後面的1是channel的數量,因為我們輸入的圖片是黑白的,因此channel是1,例如如果是RGB圖像,那麼channel就是3。
x_image = tf.reshape(xs, [-1, 28, 28, 1])

# 定義捲積層1,以5*5的面積進行掃描,因為黑白圖片channel是1所以輸入是1,輸出是32個高度的值
W_conv1 = weight_variable([5, 5, 1, 32])
# bias的大小是32個長度,因此我們傳入它的shape為[32]
b_conv1 = bias_variable([32])
# 定義好了Weight和bias,我們就可以定義捲積神經網路的第一個捲積層h_conv1=conv2d(x_image,W_conv1)+b_conv1,同時我們對h_conv1進行非線性處理,
# 也就是激活函數來處理嘍,這裡我們用的是tf.nn.relu(修正線性單元)來處理,要註意的是,因為採用了SAME的padding方式,輸出圖片的大小沒有變化依然是28x28,
# 只是厚度變厚了,因此現在的輸出大小就變成了28x28x32
h_conv1 = tf.nn.relu(conv2d(x_image, W_conv1) + b_conv1)
# 最後我們再進行pooling的處理就ok啦,經過pooling的處理,輸出大小就變為了14x14x32
h_pool1 = max_pool_2x2(h_conv1)

# 定義捲積層2
# 掃描的面積還是定義成5*5,輸入大小為32,因為捲積層1中輸出為32就被設置為這裡的輸入大小了。輸出大小設定為64,也就是變得更高了
W_conv2 = weight_variable([5, 5, 32, 64])
# bias的大小是64個長度,因此我們傳入它的shape為[64]
b_conv2 = bias_variable([64])
# 定義好了Weight和bias,我們就可以定義捲積神經網路的第二個捲積層h_conv2=conv2d(h_pool1,W_conv2)+b_conv2,同時我們對h_conv2進行非線性處理,
# 也就是激活函數來處理嘍,這裡我們用的是tf.nn.relu(修正線性單元)來處理,要註意的是,因為採用了SAME的padding方式,輸出圖片的大小沒有變化依然是在第一層捲集層輸出時的14*14,
# 只是厚度變厚了,因此現在的輸出大小就變成了14x14x64
h_conv2 = tf.nn.relu(conv2d(h_pool1, W_conv2) + b_conv2)
# 最後我們再進行pooling的處理就ok啦,經過pooling的處理,輸出大小就變為了7x7x64
h_pool2 = max_pool_2x2(h_conv2)

# 定義神經網路全連接層1
# 其形狀為h_pool2的輸出形狀7*7*64,輸出為1024個神經元
W_fc1 = weight_variable([7*7*64, 1024])
b_fc1 = bias_variable([1024])
# 把h_pool2的輸出的包含長寬平面的信息形狀轉換成一個維度的數據,相當於變平的操作:[n_samples, 7, 7, 64] => [n_samples, 7*7*64]
h_pool1_flat = tf.reshape(h_pool2, [-1, 7*7*64])
h_fc1 = tf.nn.relu(tf.matmul(h_pool1_flat, W_fc1) + b_fc1)
h_fc1_drop = tf.nn.dropout(h_fc1, keep_prob)

# 定義神經網路全連接層2
# 其輸入為全連接層1的輸出1024,輸出為0-9數字的one hot格式,因此為10列
W_fc2 = weight_variable([1024, 10])
b_fc2 = bias_variable([10])
prediction = tf.nn.softmax(tf.matmul(h_fc1_drop, W_fc2) + b_fc2)

# 定義loss值
cross_entropy = tf.reduce_mean(-tf.reduce_sum(ys * tf.log(prediction), axis=1))
# 對於比較龐大的系統可以用AdamOptimizer比較好一點
train_step = tf.train.AdamOptimizer(1e-4).minimize(cross_entropy)
# train_step = tf.train.GradientDescentOptimizer(0.5).minimize(cross_entropy)

sess = tf.Session()
init = tf.global_variables_initializer()
sess.run(init)

for i in range(1000):
    batch_xs, batch_ys = mnist.train.next_batch(100)
    sess.run(train_step, feed_dict={xs: batch_xs, ys: batch_ys, keep_prob: 0.5})
    if i % 50 == 0:
        # 每隔50條列印一下預測的準確率
        print(computer_accuracy(mnist.test.images, mnist.test.labels))

輸出為:

0.0853
0.7785
0.8835
0.9084
0.9241
0.9316
0.9412
0.9463
0.9485
0.951
0.9561
0.9578
0.9599
0.9611
0.964
0.9644
0.966
0.9673
0.9687
0.9685

這次用捲積神經網路把結果提高了很多。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • JavaScript庫是預先編寫的 JavaScript 工具代碼,讓開發者可以更容易開發 JavaScript 應用。這個列表我們列出了2017年1月份功能豐富的JavaScript庫,可以幫助您建立有效和有用的 Web 應用程式。 ...
  • 眾所周知,在ES6之前,JavaScript是沒有塊級作用域的,如下圖所示: 學過其他語言的同學肯定有點詫異,為什麼會這樣呢?因為js還是不同於其他語言的,在ES5中,只有全局作用域和函數作用域,並沒有塊作用域,當然我們可以實現塊作用域的功能。看下麵代碼: 在這段段代碼中,我們使用立即執行函數(II ...
  • 新年好呀~過個年光打游戲,function都寫不順溜了。 上一節的代碼到這裡了: 經過長長的resolve,最終也只是解析入口文件的合法路徑信息,然後調用回調函數。 接下來分析回調函數是如何處理返回結果的: 返回的結果有兩部分,一個是loader,一個是文件對應路徑。 對於入口文件的當前解析,不存在 ...
  • 在 export defaul new Router({ )} 這個路由配置中一定要加mode : ‘history’ 否者就會在路由前面預設添加# 路由跳轉的幾種方式: ...
  • ES6 模塊的設計思想,是儘量的靜態化,使得編譯時就能確定模塊的依賴關係,以及輸入和輸出的變數。 ES6 模塊不是對象,而是通過export命令顯式指定輸出的代碼,再通過import命令輸入。 需要特別註意的是,export命令規定的是對外的介面,必須與模塊內部的變數建立一一對應關係。 export ...
  • ⚠️組件的作用域是孤立的,vue解決組件傳值問題是通過props ⚠️子傳父的時候需要vm.$emit觸發實例上的事件,頁面需要定一個方法去取值 ⚠️一定要註意命名方式和書寫,例如mylChart和myl-chart dataRadio和data-radio :objline和@data-radio ...
  • 之前的文章大量的內容在和大家探討分散式存儲,接下來的章節進入了分散式計算領域。坦白說,個人之前專業的重心側重於存儲,對許多計算的內容理解可能不是和確切,如果文章中的理解有所不妥,願虛心賜教。本篇將和大家聊一聊分散式計算的一個子集: 批處理 。 批處理系統通常也叫離線系統 ,需要大量的輸入數據,運行一 ...
  • Related Links:Zuul https://github.com/Netflix/zuulCAT https://github.com/dianping/catApollo https://github.com/ctripcorp/apolloKairosDB https://github... ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...