Python 的 Keras 庫來學習手寫數字分類,將手寫數字的灰度圖像(28 像素 ×28 像素)劃分到 10 個類別
中(0~9)
神經網絡的核心組件是層(layer),它是一種數據處理模塊,它從輸入數據中提取表示,緊接着的一個例子中,將含有兩個Dense 層,它們是密集鏈接(也叫全鏈接)的神經層,最後是一個10路的softmax層,它將返回一個由 10 個機率值(總和爲 1)組成的數組。每一個機率值表示當前數字圖像屬於 10 個數字類別中某一個的機率
損失函數(loss function):網絡如何衡量在訓練數據上的性能,即網絡如何朝着正確的方向前進
優化器(optimizer):基於訓練數據和損失函數來更新網絡的機制html
from keras.datasets import mnist from keras import models from keras import layers from keras.utils import to_categorical # 加載數據 (train_images, train_labels), (test_images, test_labels) = mnist.load_data() print("訓練圖片個數與尺寸: ", train_images.shape, "標籤數: ", len(train_labels)) print("測試圖片數量與尺寸: ", test_images.shape, "標籤數: ", len(test_labels)) # 網絡架構 network = models.Sequential() network.add(layers.Dense(512, activation='relu', input_shape=(28 * 28,))) network.add(layers.Dense(10, activation="softmax")) # 編譯 network.compile(optimizer='rmsprop', loss='categorical_crossentropy', metrics=['accuracy']) # 數據預處理,將其變換爲網絡要求的形狀,並縮放到全部值都在 [0, 1] 區間 train_images = train_images.reshape((60000, 28 * 28)) train_images = train_images.astype('float32') / 255 test_images = test_images.reshape((10000, 28 * 28)) test_images = test_images.astype('float32') / 255 # 對標籤進行分類編碼 train_labels = to_categorical(train_labels) test_labels = to_categorical(test_labels) # 訓練模型,epochs表示訓練遍數,batch_size表示每次餵給網絡的數據數目 network.fit(train_images, train_labels, epochs=5, batch_size=128) # 檢測在測試集上的正確率 test_loss, test_acc = network.evaluate(test_images, test_labels) print('正確率: ', test_acc)
張量是矩陣向任意維度的推廣,僅包含一個數字的張量叫做標量,數字組成的數組叫做向量(vector)或一維張量(1D 張量)。一維張量只有一個軸
顯示數字圖片git
(train_images, train_labels), (test_images, test_labels) = mnist.load_data("/home/fan/dataset/mnist.npz") # 顯示第0個數字 import matplotlib.pyplot as plt digit = train_images[0] plt.imshow(digit, cmap=plt.cm.binary) plt.show()
一些數據張量
向量數據: 2D 張量,形狀爲 (samples, features)
時間序列數據或序列數據: 3D 張量,形狀爲 (samples, timesteps, features)
圖像: 4D 張量,形狀爲 (samples, height, width, channels) 或 (samples, channels, height, width)
視頻: 5D 張量,形狀爲 (samples, frames, height, width, channels) 或 (samples, frames, channels, height, width)算法
當時間(或序列順序)對於數據很重要時,應該將數據存儲在帶有時間軸的 3D 張量中
json
根據慣例,時間軸始終是第 2 個軸
圖像一般具備三個維度: 高度、寬度和顏色深度
灰度圖像只有一個顏色通道,所以能夠保存在 2D 張量中
4D張量表示
數組
圖像張量的形狀有兩種約定: 通道在後(channels-last)的約定(在 TensorFlow 中使用)和通道在前(channels-first)的約定(在 Theano 中使用)。TensorFlow 機器學習框架將顏色深度軸放在最後: (samples, height, width, color_depth),Theano將圖像深度軸放在批量軸以後: (samples, color_depth, height, width),Keras 框架同時支持這兩種格式
視頻數據爲 5D 張量,每一幀均可以保存在一個形狀爲 (height, width, color_depth) 的 3D 張量中,所以一系列幀能夠保存在一個形狀爲 (frames, height, width, color_depth) 的 4D 張量中,而不一樣視頻組成的批量則能夠保存在一個 5D 張量中,其形狀爲(samples, frames, height, width, color_depth)
一個以每秒 4 幀採樣的 60 秒 YouTube 視頻片斷,視頻尺寸爲 144×256,這個視頻共有 240 幀。4 個這樣的視頻片斷組成的批量將保存在形狀爲 (4, 240, 144, 256, 3)的張量中安全
若是將兩個形狀不一樣的張量相加,較小的張量會被廣播(broadcast),以匹配較大張量的形狀:網絡
a = np.array([[2, 2], [1, 1]]) c = np.array([3, 3]) print(a + c)
結果爲架構
[[5 5] [4 4]]
若是一個張量的形狀是 (a, b, ... n, n+1, ... m) ,另外一個張量的形狀是 (n, n+1, ... m) ,那麼你一般能夠利用廣播對它們作兩個張量之間的逐元素運算。廣播操做會自動應用於從 a 到 n-1 的軸app
在 Numpy、Keras、Theano 和 TensorFlow 中,都是用 * 實現逐元素乘積,在 Numpy 和 Keras 中,都是用標準的 dot 運算符來實現點積框架
a = np.array([1, 2]) b = np.array([[5], [6]]) # 輸出[17] print(a.dot(b))
張量變形是指改變張量的行和列,以獲得想要的形狀。變形後的張量的元素總個數與初始張量相同
a = np.array([[0, 1], [2, 3], [4, 5]]) print(a) print("after reshape: \n", a.reshape((2, 3)))
輸出
[[0 1] [2 3] [4 5]] after reshape: [[0 1 2] [3 4 5]]
轉置 np.transpose(x)
SGD(stochastic gradient descent) -- 隨機梯度降低
不一樣的張量格式與不一樣的數據處理類型須要用到不一樣的層,簡單的向量數據保存在形狀爲 (samples, features) 的 2D 張量中,一般用密集鏈接層[densely connected layer,也叫全鏈接層(fully connected layer)或密集層(dense layer),對應於 Keras 的 Dense 類]來處理。序列數據保存在形狀爲 (samples, timesteps, features) 的 3D 張量中,一般用循環層(recurrent layer,好比 Keras 的 LSTM 層)來處理。圖像數據保存在 4D 張量中,一般用二維卷積層(Keras 的 Conv2D )來處理
Keras框架具備層兼容性,具體指的是每一層只接受特定形狀的輸入張量,並返回特定形狀的輸出張量
layer = layers.Dense(32, input_shape=(784,))
建立了一個層,只接受第一個維度大小爲 784 的 2D 張量做爲輸入。這個層將返回一個張量,第一個維度的大小變成了 32 所以,這個層後面只能鏈接一個接受 32 維向量做爲輸入的層,使用 Keras 時,你無須擔憂兼容性,由於向模型中添加的層都會自動匹配輸入層的形狀,下一次層能夠寫爲
model.add(layers.Dense(32))
它能夠自動推導出輸入形狀等於上一層的輸出形狀
具備多個輸出的神經網絡可能具備多個損失函數(每一個輸出對應一個損失函數)。可是,梯度降低過程必須基於單個標量損失值。所以,對於具備多個損失函數的網絡,須要將全部損失函數取平均,變爲一個標量值
一個 Keras 工做流程
定義模型有兩種方法:
一種是使用 Sequential 類(僅用於層的線性堆疊,這是目前最多見的網絡架構)
另外一種是函數式 API(functional API,用於層組成的有向無環圖,讓你能夠構建任意形式的架構)
Sequential 類定義兩層模型
model = models.Sequential() model.add(layers.Dense(32, activation='relu', input_shape=(784,))) model.add(layers.Dense(10, activation='softmax'))
函數式 API 定義的相同模型
input_tensor = layers.Input(shape=(784,)) x = layers.Dense(32, activation='relu')(input_tensor) output_tensor = layers.Dense(10, activation='softmax')(x) model = models.Model(inputs=input_tensor, outputs=output_tensor)
如下學習根據電影評論的文字內容將其劃分爲正面或負面
使用 IMDB 數據集,數據集被分爲用於訓練的 25 000 條評論與用於測試的 25 000 條評論,訓練集和測試集都包含 50% 的正面評論和 50% 的負面評論
其中,數據集中的labels 都是 0 和 1 組成的列表,0表明負面(negative),1 表明正面(positive)
你不能將整數序列直接輸入神經網絡。你須要將列表轉換爲張量。轉換方法有如下兩種
訓練代碼
from keras.datasets import imdb import os import numpy as np from keras import models from keras import layers import matplotlib.pyplot as plt # 將整數序列編碼爲二進制矩陣 def vectorize_sequences(sequences, dimension=10000): results = np.zeros((len(sequences), dimension)) for i, sequence in enumerate(sequences): # results[i] 的指定索引設爲 1 results[i, sequence] = 1 return results data_url_base = "/home/fan/dataset" # 下載數據且只保留出現頻率最高的前10000個單詞 (train_data, train_labels), (test_data, test_labels) = imdb.load_data(num_words=10000, path=os.path.join(data_url_base, "imdb.npz")) # 將某條評論迅速解碼爲英文單詞 # word_index 是一個將單詞映射爲整數索引的字典 word_index = imdb.get_word_index(path=os.path.join(data_url_base, "imdb_word_index.json")) # 將整數索引映射爲單詞 reverse_word_index = dict([(value, key) for (key, value) in word_index.items()]) # 索引減去了 3,由於 0、一、2是爲「padding」(填充)、 # 「start of sequence」(序列開始)、「unknown」(未知詞)分別保留的索引 decoded_review = ' '.join([reverse_word_index.get(i - 3, '?') for i in train_data[0]]) print(decoded_review) # 將數據向量化 x_train = vectorize_sequences(train_data) x_test = vectorize_sequences(test_data) # 將標籤向量化 y_train = np.asarray(train_labels).astype('float32') y_test = np.asarray(test_labels).astype('float32') # 設計網絡 # 兩個中間層,每層都有 16 個隱藏單元 # 第三層輸出一個標量,預測當前評論的情感 # 中間層使用 relu 做爲激活函數,最後一層使用 sigmoid 激活以輸出一個 0~1 範圍內的機率值 model = models.Sequential() model.add(layers.Dense(16, activation='relu', input_shape=(10000,))) model.add(layers.Dense(16, activation='relu')) model.add(layers.Dense(1, activation='sigmoid')) # 模型編譯 # binary_crossentropy二元交叉熵 model.compile(optimizer='rmsprop', loss='binary_crossentropy', metrics=['acc']) # 留出驗證集 x_val = x_train[:10000] partial_x_train = x_train[10000:] y_val = y_train[:10000] partial_y_train = y_train[10000:] history = model.fit(partial_x_train, partial_y_train, epochs=20, batch_size=512, validation_data=(x_val, y_val)) # 獲得訓練過程當中的全部數據 history_dict = history.history print(history_dict.keys()) # 繪製訓練損失和驗證損失 loss_values = history_dict['loss'] val_loss_values = history_dict['val_loss'] epochs = range(1, len(loss_values) + 1) # 'bo' 藍色圓點 plt.plot(epochs, loss_values, 'bo', label='Training loss') # 'b' 藍色實線 plt.plot(epochs, val_loss_values, 'b', label='Validation loss') plt.title("Training and Validation loss") plt.xlabel('Epochs') plt.legend() plt.show() # 繪製訓練精度和驗證精度 # plt.clf() 清空圖像 acc = history_dict['acc'] val_acc = history_dict['val_acc'] plt.plot(epochs, acc, 'bo', label='Training acc') plt.plot(epochs, val_acc, 'b', label='Validation acc') plt.title('Training and validation accuracy') plt.xlabel('Epochs') plt.ylabel('Accuracy') plt.legend() plt.show()
結果以下
可見訓練損失每輪都在下降,訓練精度每輪都在提高,但驗證損失和驗證精度並不是如此,這是由於咱們遇到了過擬合的狀況,能夠採用多種方法防止過擬合,如增長數據樣本,減小訓練次數,減小網絡參數等
使用訓練好的網絡對新數據進行預測
model.predict(x_test)
多分類問題 -- 新聞主題分類
若是每一個數據點只能劃分到一個類別,那麼這就是一個單標籤、多分類問題,而若是每一個數據點能夠劃分到多個類別(主題),那它就是一個多標籤、多分類問題,此處爲單標籤、多分類問題
將標籤向量化有兩種方法
將標籤轉換爲整數張量
y_train = np.array(train_labels) y_test = np.array(test_labels)
對於此種編碼方法,咱們選擇的損失函數應該爲sparse_categorical_crossentropy
,該編碼方法適用於整數標籤
新聞分類示例
from keras.datasets import reuters import numpy as np from keras.utils.np_utils import to_categorical from keras import models from keras import layers import matplotlib.pyplot as plt # 將整數序列編碼爲二進制矩陣 def vectorize_sequences(sequences, dimension=10000): results = np.zeros((len(sequences), dimension)) for i, sequence in enumerate(sequences): # results[i] 的指定索引設爲 1 results[i, sequence] = 1 return results # 將數據限定爲前10000個最常出現的單詞 (train_data, train_labels), (test_data, test_labels) = reuters.load_data(num_words=10000, path="/home/fan/dataset/reuters/reuters.npz") # 新聞解析 word_index = reuters.get_word_index(path="/home/fan/dataset/reuters/reuters_word_index.json") reversed_word_index = dict([(value, key) for (key, value) in word_index.items()]) # 索引減去了3,由於 0、一、2 是爲「padding」( 填 充 )、「start of # sequence」(序列開始)、「unknown」(未知詞)分別保留的索引 decoded_newswire = ' '.join([reversed_word_index.get(i-3, '?') for i in train_data[0]]) print(decoded_newswire) # 標籤的索引範圍爲0 - 45 print(np.amax(train_labels)) # 數據向量化 x_train = vectorize_sequences(train_data) x_test = vectorize_sequences(test_data) # 標籤向量化 one_hot_train_labels = to_categorical(train_labels) one_hot_test_labels = to_categorical(test_labels) model = models.Sequential() model.add(layers.Dense(64, activation='relu', input_shape=(10000, ))) model.add(layers.Dense(64, activation='relu')) model.add(layers.Dense(46, activation='softmax')) model.compile(optimizer='rmsprop', loss='categorical_crossentropy', metrics=['accuracy']) # 留出1000驗證集 x_val = x_train[:1000] partial_x_train = x_train[1000:] y_val = one_hot_train_labels[:1000] partial_y_train = one_hot_train_labels[1000:] history = model.fit(partial_x_train, partial_y_train, epochs=20, batch_size=512, validation_data=(x_val, y_val)) loss = history.history['loss'] val_loss = history.history['val_loss'] epochs = range(1, len(loss) + 1) plt.plot(epochs, loss, 'bo', label='Training loss') plt.plot(epochs, val_loss, 'b', label='Validation loss') plt.title('Training and validation loss') plt.xlabel('Epochs') plt.ylabel('Loss') plt.legend() plt.show() acc = history.history['acc'] val_acc = history.history['val_acc'] plt.plot(epochs, acc, 'bo', label='Training acc') plt.plot(epochs, val_acc, 'b', label='Validation acc') plt.title('Training and validation accuracy') plt.xlabel('Epochs') plt.ylabel('Accuracy') plt.legend() plt.show()
實驗結果
Loss
Accuracy
要點
迴歸問題 預測一個連續值而不是離散的標籤
當咱們將取值範圍差別很大的數據輸入到神經網絡中,網絡可能會自動適應這種數據,可是學習確定是困難的。對於這種數據,廣泛採用的最佳實踐是對每一個特徵作標準化,即對於輸入數據的每一個特徵(輸入數據矩陣中的列),減去特徵平均值,再除以標準差,這樣獲得的特徵平均值爲 0,標準差爲 1
此處要注意,用於測試數據標準化的均值和標準差都是在訓練數據上計算獲得的。在工做流程中,你不能使用在測試數據上計算獲得的任何結果,即便是像數據標準化這麼簡單的事情也不行
當樣本數量不多,咱們應該使用一個很是小的網絡,否則會出現嚴重的過擬合
當進行標量回歸時,網絡的最後一層只設置一個單元,不須要激活,是一個線性層,添加激活函數將會限制輸出範圍
當你的數據量較小時,沒法給驗證集分出較大的樣本,這致使驗證集的劃分方式會形成驗證分數上有很大的方差,而沒法對模型進行有效的評估,這時咱們能夠選用K折交叉驗證
K折交叉驗證
例子
from keras.datasets import boston_housing from keras import models from keras import layers import numpy as np import matplotlib.pyplot as plt def builde_model(): model = models.Sequential() model.add(layers.Dense(64, activation='relu', input_shape=(train_data.shape[1],))) model.add(layers.Dense(64, activation='relu')) model.add(layers.Dense(1)) model.compile(optimizer='rmsprop', loss='mse', metrics=['mae']) return model (train_data, train_targets), (test_data, test_targets) = boston_housing.load_data('/home/fan/dataset/boston_housing.npz') # 數據標準化 mean = train_data.mean(axis=0) train_data -= mean std = train_data.std(axis=0) train_data /= std test_data -= mean test_data /= std k = 4 num_val_samples = len(train_data) num_epochs = 500 all_mae_histories = [] for i in range(k): print('processing fold #', i) val_data = train_data[i * num_val_samples: (i + 1) * num_val_samples] val_targets = train_targets[i * num_val_samples: (i + 1) * num_val_samples] partial_train_data = np.concatenate([train_data[:i * num_val_samples], train_data[(i + 1) * num_val_samples:]], axis=0) partial_train_targets = np.concatenate([train_targets[:i * num_val_samples], train_targets[(i + 1) * num_val_samples:]], axis=0) model = builde_model() # 靜默模式 verbose = 0 history = model.fit(partial_train_data, partial_train_targets, validation_data=(val_data, val_targets), epochs=num_epochs, batch_size=1, verbose=0) print(history.history.keys()) if 'mean_absolute_error' not in history.history.keys(): continue mae_history = history.history['mean_absolute_error'] all_mae_histories.append(mae_history) average_mae_history = [np.mean([x[i] for x in all_mae_histories]) for i in range(num_epochs)] def smooth_curve(points, factor=0.9): smoothed_points = [] for point in points: if smoothed_points: previous = smoothed_points[-1] smoothed_points.append(previous * factor + point * (1 - factor)) else: smoothed_points.append(point) return smoothed_points smooth_mae_history = smooth_curve(average_mae_history[10:]) plt.plot(range(1, len(smooth_mae_history) + 1), smooth_mae_history) plt.xlabel('Epochs') plt.ylabel('Validation mae') plt.show()
實驗結果
每一個數據點爲前面數據點的指數移動平均值
機器學習的四個分支
監督學習 給定一組樣本(一般由人工標註),它能夠學會將輸入數據映射到已知目標,如
分類
迴歸
序列生成 給定一張圖像,預測描述圖像的文字
語法樹預測 給定一個句子,預測其分解生成的語法樹
目標檢測 給定一張圖像,在圖中特定目標的周圍畫一個邊界框
圖像分割 給定一張圖像,在特定物體上畫一個像素級的掩模
無監督學習 在沒有目標的狀況下尋找輸入數據的有趣變換,其目的在於數據可視化、數據壓縮、數據去噪或更好地理解數據中的相關性,如
降維
聚類
自監督學習 是沒有人工標註的標籤的監督學習,標籤仍然存在,但它們是從輸入數據中生成的,一般是使用啓發式算法生成的,如
自編碼器其生成的目標就是未經修改的輸入
給定視頻中過去的幀來預測下一幀或者給定文本中前面的詞來預測下一個詞(用將來的輸入數據做爲監督)
強化學習
在強化學習中,智能體(agent)接收有關其環境的信息,並學會選擇使某種獎勵最大化的行動
機器學習的目的是獲得能夠泛化(generalize)的模型,即在前所未見的數據上表現很好的模型,而過擬合則是核心難點
評估模型的重點是將數據劃分爲三個集合: 訓練集、驗證集和測試集
劃分爲這三個集合的緣由是:
訓練集用來訓練網絡中的參數,驗證集用來調節網絡超參數,測試集用來測試網絡性能,須要注意的是咱們不該該使用模型讀取任何測試集相關的信息而後依此來調節模型
若是可用的數據相對較少,而你又須要儘量精確地評估模型,那麼能夠選擇帶有打亂數據的重複 K 折驗證(iterated K-fold validation with shuffling)
具體作法:屢次使用 K 折驗證,在每次將數據劃分爲 K 個分區以前都先將數據打亂。最終分數是每次 K 折驗證分數的平均值,這種方法一共要訓練和評估 P×K 個模型,計算代價很大
選擇模型評估方法時,須要注意如下幾點:
數據表明性
訓練的數據要可以表明總體,這時應該將數據打亂
時間箭頭
當數據包含數據信息時,應該始終確保測試集中全部數據的時間都晚於訓練集數據
數據冗餘
當存在數據冗餘時,打亂數據可能會形成訓練集和驗證集出現重複的數據,而咱們要確保訓練集和驗證集之間沒有交集
將數據輸入神經網絡以前,通常咱們都須要進行數據預處理,以使其與咱們模型須要輸入類型相匹配,包括
向量化
神經網絡的全部輸入和目標都必須是浮點數張量
值標準化
輸入數據應該具備如下特徵
取值較小: 大部分值都應該在 0~1 範圍內
同質性(homogenous): 全部特徵的取值都應該在大體相同的範圍內
一種更嚴格的標準化爲將: 每一個特徵分別標準化,使其均值爲 0,標準差爲 1
處理缺失值
通常來講,對於神經網絡,將缺失值設置爲 0 是安全的,只要 0 不是一個有意義的值。網絡可以從數據中學到 0 意味着缺失數據,而且會忽略這個值
可是當網絡在沒有缺失值的狀況下訓練,這時候網絡就不能學會忽略缺失值,這時咱們須要人爲生成一些有缺失項的訓練樣本
特徵工程(feature engineering)是指將數據輸入模型以前,利用你本身關於數據和機器學習算法(這裏指神經網絡)的知識對數據進行硬編碼的變換(不是模型學到的),以改善模型的效果
良好的特徵可讓你用更少的數據、更少的資源、更優雅地解決問題
優化(optimization)是指調節模型以在訓練數據上獲得最佳性能(即機器學習中的學習),而泛化(generalization)是指訓練好的模型在前所未見的數據上的性能好壞。機器學習的目的固然是獲得良好的泛化
訓練開始時,優化和泛化是相關的: 訓練數據上的損失越小,測試數據上的損失也越小。這時的模型是欠擬合(underfit)的,即仍有改進的空間,網絡尚未對訓練數據中全部相關模式建模;但在訓練數據上迭代必定次數以後,泛化再也不提升,驗證指標先是不變,而後開始變差,即模型開始過擬合。這時模型開始學習僅和訓練數據有關的模式,但這種模式對新數據來講是錯誤的或可有可無的
防止過擬合的方法:
減少網絡大小
防止過擬合的最簡單的方法就是減少模型大小,即減小模型中可學習參數的個數
要找到合適的模型大小,通常的工做流程是開始時選擇相對較少的層和參數,而後逐漸增長層的大小或增長新層,直到這種增長對驗證損失的影響變得很小
添加權重正則化
理論:簡單模型比複雜模型更不容易過擬合
此處簡單模型指參數值分佈的熵更小的模型或參數更少的模型
方法:強制讓模型權重只能取較小的值,從而限制模型的複雜度
如 Lp正則化
L1 正則化(L1 regularization):添加的成本與權重係數的絕對值成正比
L2 正則化(L2 regularization):添加的成本與權重係數的平方成正比
添加 dropout 正則化
訓練過程當中隨機將該層的一些輸出特徵捨棄
Keras添加正則化的方式
model.add(layers.Dense(16, kernel_regularizer=regularizers.l2(0.001), activation='relu', input_shape=(10000,)))
其中0.001爲正則化損失係數
因爲這個懲罰項只在訓練時添加,因此這個網絡的訓練損失會比測試損失大不少
若是使用dropout正則化的話,dropout 比率(dropout rate)是被設爲 0 的特徵所佔的比例,一般在 0.2~0.5範圍內。測試時沒有單元被捨棄,而該層的輸出值須要按 dropout 比率縮小,由於這時比訓練時有更多的單元被激活,須要加以平衡
在 Keras 中,你能夠經過 Dropout 層向網絡中引入 dropout,dropout 將被應用於前面一層的輸出
model.add(layers.Dropout(0.5))
經常使用的由問題類型選擇的最後一層激活和損失函數
問題類型 | 最後一層激活 | 損失函數 |
---|---|---|
二分類問題 | sigmoid | binary_crossentropy |
多分類、單標籤問題 | softmax | categorical_crossentropy |
多分類、多標籤問題 | sigmoid | binary_crossentropy |
迴歸到任意值 | 無 | mse |
迴歸到 0~1 範圍內的值 | sigmoid | mse 或 binary_crossentropy |
在模型確認以後,咱們須要優化咱們的網絡以達到最佳的性能,此時能夠嘗試如下幾項: