日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > python >内容正文

python

Python机器学习笔记:深入理解Keras中序贯模型和函数模型

發布時間:2025/7/14 python 33 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Python机器学习笔记:深入理解Keras中序贯模型和函数模型 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

  ?先從sklearn說起吧,如果學習了sklearn的話,那么學習Keras相對來說比較容易。為什么這樣說呢?

  我們首先比較一下sklearn的機器學習大致使用流程和Keras的大致使用流程:

sklearn的機器學習使用流程:

from sklearn.模型簇 import 模型名 from sklearn.metrics import 評價指標''' 數據預處理及訓練測試集分離提取'''myModel = 模型名稱() # 對象初始化 myModel.fit(訓練集x , 訓練集y) #模型訓練 y預測集 = myModel.predict(開發集x) #模型預測 評價指標 = 評價指標(y預測集,y測試集) #模型效果評估

  

Keras的機器學習使用流程:

import keras ...根據具體需求引入keras的包......keras模型搭建... ...keras模型編譯(可選擇模型指標)...kerasModel.fit(訓練集x,訓練集y)#keras模型訓練 y預測集=myModel.predict(開發集x)#keras模型預測

  

兩者的區別

  由上面偽代碼可知Keras和sklearn最大不同在于需要進行模型搭建,可是既然有了這么多模型為什么還要模型搭建?

  如果你了解過神經網絡感知機就會比較理解這個過程,一個感知器相當于一個神經元,可根據輸入信息反饋出需要的電信號,根據我們的世界觀,一個細胞可以單獨執行很多功能但是大量單純的任務會讓細胞只針對一個方向發展。用生物學的說話就是分化能力逐漸減弱,機器學習說法就是過擬合。因此,只有大量細胞通過不同的組合才能完成紛繁復雜的預測任務,因而有證明說神經網絡理論上可擬合出任何曲線。

  那么話說回來,Keras需要自行搭建模型,搭建方法有兩種:序貫模型和函數式模型。而我本次的筆記就是學習序貫模型和函數式模型。

序貫模型

  序貫模型是多個網絡蹭的線性堆疊,是函數式模型的簡略版,為最簡單的線性,從頭到尾的結構順序,不發生分叉。是多個網絡層的線性堆疊。

  Keras實現了很多層,包括core核心層,Convolution卷積層,Pooling池化層等非常豐富有趣的網絡結構。

應用序貫模型的基本步驟

  • 1,model.add()?    ? ? ??添加層
  • 2,model.compile()     模型訓練的BP模式設置
  • 3,model.fit()      ? ?模型訓練參數設置+訓練
  • 4,model.evaluate()? ?   ? ?模型評估
  • 5,model.predict()     模型預測

序貫模型的創建

  1,可以通過向Sequential模型傳遞一個layer的list來構造Sequential模型:

from keras.models import Sequential from keras.layers import Dense ,Activationmodel = Sequential([Dense(32,input_shape=(784,)),Activation('relu'),Dense(10),Activation('softmax') ])model.summary()

  

  2,也可以通過.add()方法一個個的將layer加入模型中:

from keras.models import Sequential from keras.layers import Dense ,Activationmodel = Sequential() model.add(Dense(32,input_shape=(784,))) model.add(Activation('relu')) model.add(Dense(10)) model.add(Activation('softmax'))model.summary()

  結果如下:

Using TensorFlow backend. _________________________________________________________________ Layer (type) Output Shape Param # ================================================================= dense_1 (Dense) (None, 32) 25120 _________________________________________________________________ activation_1 (Activation) (None, 32) 0 _________________________________________________________________ dense_2 (Dense) (None, 10) 330 _________________________________________________________________ activation_2 (Activation) (None, 10) 0 ================================================================= Total params: 25,450 Trainable params: 25,450 Non-trainable params: 0 _________________________________________________________________

  

3,指定輸入數據的shape

  模型需要知道輸入數據的shape,因此,Sequential的第一層需要接受一個關于輸入數據shape的參數,后面的各個層則可以自動的推導出中間數據的shape,因此不需要為每一層都指定這個參數。有幾種方法來為第一層指定輸入數據的shape

  1,傳遞一個input_shape的關鍵字參數給第一層,input_shape是一個tupel類型的數據(一個整數或者None的元祖,其中None表示可能為任何正整數)在input_shape中不包含數據的batch大小。

model = Sequential() model.add(Dense(64,input_shape=(20,),activation='relu'))

  

  2,有些2D層,如Dense,支持通過指定其輸入維度input_dim來隱含的指定輸入數據shape,是一個Int類型的數據。一些3D的時域層支持通過參數input_dim和input_length來指定輸入shape。

model = Sequential() model.add(Dense(64, input_dim=20, activation='relu'))

  3,如果你需要為你的輸入指定一個固定的batch大小(這對于statsful RNNs很有用),你可以傳遞一個batch_size參數給一個層。如果你同時將batch_size=32和input_shape = (6,8)傳遞給一個層,那么每一批輸入的尺寸就是(32,6,8)。

  因此下面的代碼是等價的:

model = Sequential() model.add(Dense(32, input_shape=(784,)))model = Sequential() model.add(Dense(32, input_dim=784))

  下面三種方法也是嚴格等價的

model = Sequential() model.add(LSTM(32, input_shape=(10, 64)))model = Sequential() model.add(LSTM(32, batch_input_shape=(None, 10, 64)))model = Sequential() model.add(LSTM(32, input_length=10, input_dim=64))

  

4,編譯

  在訓練模型之前,我們需要通過compile來對學習過程進行配置,compile接收三個參數:優化器optimizer,損失函數loss,指標列表metrics。

compile(self, optimizer, loss, metrics=None, sample_weight_mode=None)

其中:

  optimizer:字符串(預定義優化器名)或者優化器對象,,如?rmsprop?或?adagrad,也可以是 Optimizer 類的實例。詳見:optimizers。

  loss:字符串(預定義損失函數名)或目標函數,模型試圖最小化的目標函數,它可以是現有損失函數的字符串標識符,如categorical_crossentropy?或?mse,也可以是一個目標函數。詳見:losses。

  metrics:列表,包含評估模型在訓練和測試時的網絡性能的指標,典型用法是metrics=[‘accuracy’]。評估標準可以是現有的標準的字符串標識符,也可以是自定義的評估標準函數。

注意:

  模型在使用前必須編譯,否則在調用fit或者evaluate時會拋出異常。

例子:

# 多分類問題 model.compile(optimizer='rmsprop',loss='categorical_crossentropy',metrics=['accuracy'])# 二分類問題 model.compile(optimizer='rmsprop',loss='binary_crossentropy',metrics=['accuracy'])# 均方誤差回歸問題 model.compile(optimizer='rmsprop',loss='mse')# 自定義評估標準函數 import keras.backend as Kdef mean_pred(y_true, y_pred):return K.mean(y_pred)model.compile(optimizer='rmsprop',loss='binary_crossentropy',metrics=['accuracy', mean_pred])

  

5,訓練

  Keras 模型在輸入數據和標簽的 Numpy 矩陣上進行訓練。為了訓練一個模型,你通常會使用?fit?函數。文檔詳見此處。

fit(self, x, y, batch_size=32, epochs=10, verbose=1, callbacks=None,validation_split=0.0, validation_data=None, shuffle=True,class_weight=None, sample_weight=None, initial_epoch=0)

  本函數將模型訓練nb_epoch輪,其參數有:

  x:輸入數據,如果模型只有一個輸入,那么x的類型是numpy array,如果模型有多個輸入,那么x的類型應當是list,list的元素是對應于各個輸入的numpy array

  y:標簽 ,numpy array

  batch_size:整數,指定進行梯度下降時每個batch包含的樣本數,訓練時一個batch的樣本會被計算一次梯度下降,使目標函數優化一步。

  epochs:整數,訓練的輪數,每個epoch會把訓練集輪一遍。

  verbose:日志顯示,0為不在標準輸出流輸出日志信息,1為輸出進度條記錄,2為每個epoch輸出一行記錄

  callbacks:list,,其中的元素是keras.callbacks.Callback的對象。這個list中的回調函數將會在訓練過程中的適當時機被調用,參考回調函數。

  validation_split:0~1之間的浮點數,用來指定訓練集的一定比例數據作為驗證集。驗證集將不參與訓練,并在每個epoch結束后測試的模型的指標,如損失函數,精確度等。注意,validation_split的劃分在shuffle之前,因此如果你的數據本身是有序的,需要先手工打亂再指定validation_split,否則可能會出現驗證集樣本不均勻。

  validation_data:形式為(X,y)的tuple,是指定的驗證集,此參數將覆蓋validation_spilt。

  shuffle:布爾值或者字符串,一般為布爾值,表示是否在訓練過程中隨機打亂輸入樣本的順序。若為字符串“batch”,則用來處理HDF5數據大特殊情況,它將在batch內部將數據打亂。

  class_weight:字典,將不同的類別映射為不同的權重,該參數用來訓練過程中調整損失函數(只能用于訓練)

  sample_weight:權值的numpy array,用于在訓練時調整損失(僅用于訓練)。

可以傳遞一個1D的與樣本等長的向量用于對樣本進行1對1的加權,或者在面對時序數據時,傳遞一個的形式為(samples,sequence_length)的矩陣來為每個時間步上的樣本賦不同的權。這種情況下請確定在編譯模型時添加了sample_weight_mode=‘temporal’。
  initial_epoch:從該參數指定的epoch開始訓練,在繼續之前的訓練時候有用。

  fit函數返回一個History的對象,其History.history屬性記錄了損失函數和其他指標的數值隨著epoch變化的情況,如果有驗證集的話,也包含了驗證集的這些指標變化情況。

?示例一:

# 對于具有2個類的單輸入模型(二進制分類):model = Sequential() model.add(Dense(32, activation='relu', input_dim=100)) model.add(Dense(1, activation='sigmoid')) model.compile(optimizer='rmsprop',loss='binary_crossentropy',metrics=['accuracy'])# 生成虛擬數據 import numpy as np data = np.random.random((1000, 100)) labels = np.random.randint(2, size=(1000, 1))# 訓練模型,以 32 個樣本為一個 batch 進行迭代 model.fit(data, labels, epochs=10, batch_size=32)

  

示例二:

# 對于具有10個類的單輸入模型(多分類分類):model = Sequential() model.add(Dense(32, activation='relu', input_dim=100)) model.add(Dense(10, activation='softmax')) model.compile(optimizer='rmsprop',loss='categorical_crossentropy',metrics=['accuracy'])# 生成虛擬數據 import numpy as np data = np.random.random((1000, 100)) labels = np.random.randint(10, size=(1000, 1))# 將標簽轉換為分類的 one-hot 編碼 one_hot_labels = keras.utils.to_categorical(labels, num_classes=10)# 訓練模型,以 32 個樣本為一個 batch 進行迭代 model.fit(data, one_hot_labels, epochs=10, batch_size=32)

  

6,評估

  根據驗證集評估模型的好壞

evaluate(self, x, y, batch_size=32, verbose=1, sample_weight=None)

  本函數按batch計算在某些輸入數據上模型的誤差,其參數有:

  x:輸入數據,與fit一樣,是numpy array 或者numpy array的list

  y:標簽,numpy array

  batch_size:整數,含義同fit的同名參數

  verbose:含義同fit的同名參數,但是只能取0或1

  sample_weight:numpy array ,含義同fit的同名參數

  本函數返回一個測試誤差的標量值(如果模型沒有其他評價指標),或一個標量的list(如果模型還有其他的評價指標)。model.metrics_names將給出list中各個值的含義。

  如果沒有特殊說明,以下函數的參數均保持與fit的同名參數相同的含義

  如果沒有特殊說明,以下函數的verbose參數(如果有)均只能取0或者1

score = model.evaluate(x_val , y_val ,batch_size = 128) print('val score:', score[0]) print('val accuracy:', score[1])

  

7,預測

  對已經訓練完成的模型,輸入特征值x會預測得到標簽y。

predict(self, x, batch_size=32, verbose=0) predict_classes(self, x, batch_size=32, verbose=1) predict_proba(self, x, batch_size=32, verbose=1)

  本函數按batch獲得輸入數據對應的輸出,其參數有:

  函數的返回值是預測值的numpy array

  predict_classes:本函數按batch產生輸入數據的類別預測結果

  predict_proba:本函數按batch產生輸入數據屬于各個類別的概率

x = 1 y = model.predict(x,verbose=0) print(y)

  

8,on_batch,batch的結果,檢查

train_on_batch(self, x, y, class_weight=None, sample_weight=None) test_on_batch(self, x, y, sample_weight=None) predict_on_batch(self, x)

  train_on_batch:本函數在batch的數據上進行一次參數更新,函數返回訓練誤差的標量值或者標量值的list,與evaluate的情形相同。

  test_on_batch:本函數在一個batch的樣本上對模型進行評估,函數的返回與evaluate的情形相同。

  predict_on_batch:本函數在一個batch的樣本上對模型進行測試,函數返回模型在一個batch上的預測結果。

?

9,完整代碼及其一次結果

代碼:

from keras.models import Sequential from keras.layers import Dense ,Activation,Dropout import numpy as np# 準備訓練集和驗證集 x_train = np.random.random((1000,20)) y_train = np.random.randint(2,size=(1000,1)) x_val = np.random.random((100,20)) y_val = np.random.randint(2,size=(100,1))model = Sequential() model.add(Dense(64,input_dim=20,activation='relu')) model.add(Dropout(0.5)) model.add(Dense(64,activation='relu')) model.add(Dropout(0.5)) model.add(Dense(1,activation='sigmoid'))model.compile(loss='binary_crossentropy',optimizer='rmsprop',metrics=['accuracy']) model.fit(x_train,y_train,epochs=20,batch_size=128)score = model.evaluate(x_val , y_val ,batch_size = 128) print('val score:', score[0]) print('val accuracy:', score[1])# x = 1 # y = model.predict(x,verbose=0) # print(y)

?

結果:

Using TensorFlow backend. Epoch 1/20128/1000 [==>...........................] - ETA: 1s - loss: 0.7093 - acc: 0.5469 1000/1000 [==============================] - 0s 291us/step - loss: 0.7098 - acc: 0.5090 Epoch 2/20128/1000 [==>...........................] - ETA: 0s - loss: 0.7191 - acc: 0.4766 1000/1000 [==============================] - 0s 15us/step - loss: 0.7087 - acc: 0.5080 Epoch 3/20128/1000 [==>...........................] - ETA: 0s - loss: 0.7009 - acc: 0.4766 1000/1000 [==============================] - 0s 15us/step - loss: 0.6969 - acc: 0.5040 Epoch 4/20128/1000 [==>...........................] - ETA: 0s - loss: 0.6946 - acc: 0.5312 1000/1000 [==============================] - 0s 15us/step - loss: 0.6969 - acc: 0.5240 Epoch 5/20128/1000 [==>...........................] - ETA: 0s - loss: 0.7029 - acc: 0.4609 1000/1000 [==============================] - 0s 15us/step - loss: 0.7002 - acc: 0.4950 Epoch 6/20128/1000 [==>...........................] - ETA: 0s - loss: 0.7027 - acc: 0.4531 1000/1000 [==============================] - 0s 15us/step - loss: 0.6992 - acc: 0.5090 Epoch 7/20128/1000 [==>...........................] - ETA: 0s - loss: 0.6907 - acc: 0.5312 1000/1000 [==============================] - 0s 16us/step - loss: 0.6895 - acc: 0.5290 Epoch 8/20128/1000 [==>...........................] - ETA: 0s - loss: 0.6906 - acc: 0.5000 1000/1000 [==============================] - 0s 16us/step - loss: 0.6969 - acc: 0.5040 Epoch 9/20128/1000 [==>...........................] - ETA: 0s - loss: 0.6860 - acc: 0.5078 1000/1000 [==============================] - 0s 16us/step - loss: 0.6914 - acc: 0.5280 Epoch 10/20128/1000 [==>...........................] - ETA: 0s - loss: 0.6784 - acc: 0.6016 1000/1000 [==============================] - 0s 17us/step - loss: 0.6912 - acc: 0.5390 Epoch 11/20128/1000 [==>...........................] - ETA: 0s - loss: 0.6812 - acc: 0.6406 1000/1000 [==============================] - 0s 16us/step - loss: 0.6874 - acc: 0.5330 Epoch 12/20128/1000 [==>...........................] - ETA: 0s - loss: 0.6997 - acc: 0.4766 1000/1000 [==============================] - 0s 16us/step - loss: 0.6949 - acc: 0.5080 Epoch 13/20128/1000 [==>...........................] - ETA: 0s - loss: 0.6843 - acc: 0.5781 1000/1000 [==============================] - 0s 15us/step - loss: 0.6912 - acc: 0.5380 Epoch 14/20128/1000 [==>...........................] - ETA: 0s - loss: 0.6746 - acc: 0.5703 1000/1000 [==============================] - 0s 17us/step - loss: 0.6873 - acc: 0.5360 Epoch 15/20128/1000 [==>...........................] - ETA: 0s - loss: 0.6959 - acc: 0.5000 1000/1000 [==============================] - 0s 16us/step - loss: 0.6891 - acc: 0.5310 Epoch 16/20128/1000 [==>...........................] - ETA: 0s - loss: 0.6780 - acc: 0.5938 1000/1000 [==============================] - 0s 17us/step - loss: 0.6907 - acc: 0.5280 Epoch 17/20128/1000 [==>...........................] - ETA: 0s - loss: 0.6835 - acc: 0.5938 1000/1000 [==============================] - 0s 16us/step - loss: 0.6858 - acc: 0.5690 Epoch 18/20128/1000 [==>...........................] - ETA: 0s - loss: 0.6845 - acc: 0.4922 1000/1000 [==============================] - 0s 16us/step - loss: 0.6921 - acc: 0.5220 Epoch 19/20128/1000 [==>...........................] - ETA: 0s - loss: 0.6861 - acc: 0.5625 1000/1000 [==============================] - 0s 15us/step - loss: 0.6859 - acc: 0.5540 Epoch 20/20128/1000 [==>...........................] - ETA: 0s - loss: 0.6959 - acc: 0.5312 1000/1000 [==============================] - 0s 16us/step - loss: 0.6841 - acc: 0.5530100/100 [==============================] - 0s 440us/step val score: 0.6957631707191467 val accuracy: 0.5099999904632568

?

函數式模型

  比序貫模型要復雜,但是效果很好,可以同時/分階段輸入變量,分階段輸出想要的模型。

  所以說,只要你的模型不是類似VGG一樣

1,應用函數式模型的基本步驟

1,model.layers()? ? ?添加層

2,model.compile()? 模型訓練的BP模式設置

3,model.fit()? ? ?  模型訓練參數設置+訓練

4,evaluate()? ? ? ? ? ? ?模型評估

5,predict()? ? ? ? ? ? ? ? 模型預測

 

2,常用Model屬性

model.layers:組成模型圖的各個層 model.inputs:模型的輸入張量列表 model.outputs:模型的輸出張量列表

  

model = Model(inputs=, outputs = )

  

3,指定輸入數據的shape

inputs = Input(shape = (20, ))

 

4,編譯,訓練,評估,預測等步驟與序貫式模型相同(這里不再贅述)

compile(self, optimizer, loss, metrics=None, loss_weights=None, sample_weight_mode=None)

  本函數按batch計算在某些輸入數據上模型的誤差,其參數有:

  x:輸入數據,與fit一樣,是numpy array或者numpy array的list

  y:標簽,numpy array

  batch_size:整數,含義同fit的同名函數

  verbose:含義與fit的同名函數,但是只能取0或者1

  sample_weight:numpy array,含義同fit的同名函數

  本函數編譯模型以供訓練,參數有:

evaluate(self, x, y, batch_size=32, verbose=1, sample_weight=None)

  

5,示例一(基于上面序貫模型進行改造)

?

import numpy as np from keras.models import Model from keras.layers import Dense , Dropout,Input# 準備訓練集和驗證集 x_train = np.random.random((1000,20)) y_train = np.random.randint(2,size=(1000,1)) x_val = np.random.random((100,20)) y_val = np.random.randint(2,size=(100,1))inputs = Input(shape = (20,)) x = Dense(64,activation='relu')(inputs) x = Dropout(0.5)(x) x = Dense(64,activation='relu')(x) x = Dropout(0.5)(x) predictions = Dense(1,activation='sigmoid')(x) model=Model(inputs=inputs, outputs=predictions) model.compile(loss='binary_crossentropy', optimizer='rmsprop', metrics=['accuracy']) model.fit(x_train, y_train,epochs=20,batch_size=128)score = model.evaluate(x_val, y_val, batch_size=128) print('val score:', score[0]) print('val accuracy:', score[1])

  

 

序貫模型和函數模型共同的API

  model.summary():打印出模型的概況,它實際調用的是keras.utils.print_summary

  model.get_config():返回包含模型配置信息的Python字典,模型也可以從config中重構回去。

config = model.get_config() model = Model.from_config(config) model = Sequential.from_config(config)

  上面是分別對序貫模型和函數式模型載入config

model.get_layer():依據層名或下標獲得層對象

model.get_weights():返回模型權重張量的列表,類型為numpy.array

model.set_weights():從numpy array里載入給模型,要求數組與model.get_weights()一樣

model.to_json():返回代表模型的JSON字符串,僅僅包含網絡結構,不包含權重,可以從JSON字符串中重構模型

?

Keras模型保存

  首先不推薦使用pickle或者cPickle來保存Keras模型

1,保存結構

?  可以使用model.save(filepath)將Keras模型和權重保存在一個HDF5文件中,該文件將包含:

  • 模型的結構,以便重構該模型
  • 模型的權重
  • 訓練配置(損失函數,優化器等)
  • 優化器的狀態,以便從上次訓練中斷的地方開始

  使用Keras.models.load_model(filepath)來重新實例化你的模型,如果文件中存儲了訓練配置的話,該函數還會同時完成模型的編譯。例如:

from keras.models import load_model# create a HDF5 file 'my_load.h5' model.save('my_load.h5')# deletes the existing model del model# returns a compiled model # indentical to the previous one model = load_model('my_load.h5')

  

2,保存結構

  如果你只是希望保存模型的結構,而不包括其權重或者配置信息,可以使用:

# save as JSON json_string = model.to_json()# save as YAML yaml_string = model.to_yaml()

  這項操作可以將模型序列化為json或者yaml文件,如果需要的話你甚至可以手動打開這些文件進行編輯。

  當然你也可以從保存好的json文件或者yaml文件中載入模型:

# model reconstruction from JSON: from keras.models import model_from_json model = model_from_json(json_string)# model reconstruction from YAML model = model_from_yaml(yaml_string)

  

3,保存模型的權重

  如果需要保存模型,可通過下面的代碼利用HDF5進行保存。注意,在使用前需要確保你已經安裝了HDF5 和Python的庫h5py。

model.save_weights('my_model_weights.h5')

  如果你需要在代碼中初始化一個完全相同的模型,請使用:

model.load_weights('my_model_weights.h5')

  

4,加載權重到不同的網絡結構

  如果你需要加載權重到不同的網絡結構(有些層一樣)中,例如fine-tune或transfer-learning,你可以通過層名字來加載模型。

model.load_weights('my_model_weights.h5', by_name=True)

  例如:

""" 假如原模型為:model = Sequential()model.add(Dense(2, input_dim=3, name="dense_1"))model.add(Dense(3, name="dense_2"))...model.save_weights(fname) """ # new model model = Sequential() model.add(Dense(2, input_dim=3, name="dense_1")) # will be loaded model.add(Dense(10, name="new_dense")) # will not be loaded# load weights from first model; will only affect the first layer, dense_1. model.load_weights(fname, by_name=True)

  加載權重到不同的網絡結構上多數用于遷移學習。

?

參考文獻:https://zhuanlan.zhihu.com/p/37376691

https://zhuanlan.zhihu.com/p/50543770

轉載于:https://www.cnblogs.com/wj-1314/p/9967480.html

《新程序員》:云原生和全面數字化實踐50位技術專家共同創作,文字、視頻、音頻交互閱讀

總結

以上是生活随笔為你收集整理的Python机器学习笔记:深入理解Keras中序贯模型和函数模型的全部內容,希望文章能夠幫你解決所遇到的問題。

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