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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

【TensorFlow】笔记1:入门笔记

發布時間:2025/3/19 编程问答 16 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【TensorFlow】笔记1:入门笔记 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

文章目錄

  • 零、基礎
    • 1、系統架構
    • 2、設計理念
    • 3、常用概念
      • (1)邊
      • (2)節點
      • (3)圖
      • (4)會話
      • (5)設備
      • (6)變量——tf.Variable()
      • (7)內核
  • 一、計算模型——計算圖
    • 1、計算圖
    • 2、計算圖使用
      • (1)計算階段
      • (2)相關設置
      • (3)TF 中維護的集合列表
  • 二、數據模型——張量
    • 1、張量的概念
    • 2、張量的使用
      • (1)對中間計算結果的引用
      • (2)獲取計算結果
  • 三、運行模型——會話(session)
    • 1、兩種模式
    • 2、默認會話設置
    • 3、配置會話
  • 四、實現神經網絡
    • 1、過程
    • 2、前向傳播算法
    • 3、神經網絡參數 與 TensorFlow變量
      • (1)tf.Variable(變量)
      • (2)常數初始化
      • (3)偏置項(bias)設置
      • (4)其他變量的初始值進行初始化
      • (5)樣例
    • 4、通過Tensorflow訓練神經網絡模型
    • 5、完整代碼

零、基礎

1、系統架構

TF的系統架構,從底向上分為設備管理和通信層、數據操作層、圖計算層、API接口層、應用層。其中設備管理和通信層、數據操作層、圖計算層是TF的核心層。

  • 底層設備通信層負責網絡通信和設備管理。 設備管理可以實現TF設備異構的特性,支持CPU、GPU、Mobile等不同設備。網絡通信依賴gRPC通信協議實現不同設備間的數據傳輸和更新。
  • 第二層是數據操作層。主要包括卷積函數、激活函數等操作。
  • 第三層是圖計算層(Graph),包含本地計算流圖和分布式計算流圖的實現。Graph模塊包含Graph的創建、編譯、優化和執行等部分。
  • 第四層是API接口層。Tensor C API是對TF功能模塊的接口封裝,便于其他語言平臺調用。
  • 第四層以上是應用層。不同編程語言在應用層通過API接口層調用TF核心功能實現相關實驗和應用。

2、設計理念

  • 將圖的定義和圖的運行完全分開。TF是符號式編程(運行速度快些),符號式計算一般先定義各種變量,然后建立一個數據流圖,在數據流圖中規定各個變量之間的計算關系,最后需要對數據流圖進行編譯,注意:此時的數據流圖中沒有任何實質的數據,只有把需要運行的輸入放進去,才能在整個模型中形成數據流,從而形成輸出值。
  • TF中涉及的運算都放在圖中,而圖的運行只發生在會話(session)中。會話啟動后,就可以用數據填充節點,進行計算;關閉會話后,不能再計算。

3、常用概念

(1)邊

邊有兩種連接關系:數據依賴和控制依賴。

  • 實線表示數據依賴,代表數據(tensor)。
  • 虛線表示控制依賴,用于控制操作的運行,用于確保happens-before關系,這類邊上沒有數據流過,但源節點必須在目的節點開始執行前完成執行。常用以下代碼:
tf.Graph.control_dependencies(control_inputs)

(2)節點

節點:代表一個操作,一般表示施加的數學運算,也可以表示數據輸入(feed in)的起點以及輸出(push out)的終點,或是讀取/寫入持久變量的終點。

(3)圖

將各個節點構建成一個有向無環圖。

(4)會話

啟動圖的第一步就是創建一個Session對象。會話提供在圖中執行操作的一些方法。一般的模式是,建立會話,此時會生成一張空圖,在會話中添加節點和邊,形成一張圖,然后執行。

import tensorflow as tfmatrix1 = tf.constant([[3., 3.]]) matrix2 = tf.constant([2.], [2.])product = tf.matmul(matrix1, matrix2)with tf.Session() as sess:result = sess.run([product])print(result)

主要有兩個API接口:

  • extend:在Graph中注入數據進行計算。
  • run:輸入計算的節點和填充(傳入tensor)必要的數據后,進行運算,并輸出運算結果。

(5)設備

設備(device): 一塊可以用于運算并擁有自己的地址空間的硬件,eg: GPU和CPU。

設備的設置:with tf.device("/gpu:1"):

(6)變量——tf.Variable()

變量(variable)在圖中有固定的位置,在創建時需要一個初始值,初始值的形狀和類型決定該變量的形狀來類型。

state = tf.Variable(0, name = "counter") # 定義一個變量,初始化為標量0 input1 = tf.constant(3.0)  # 定義一個常量張量

填充機制:tf.placeholder() 臨時代替任意操作的張量,在調用Session對象的run()方法執行圖時,使用填充數據(feed_dict)作為調用參數,在結束調用后參數消失。

input1 = tf.placeholder(tf.float32) input2 = tf.placeholder(tf.float32) output = tf.mul(input1, input2) with tf.Session() as sess:print(sess.run([output], feed_dict={input1:[7.], input2:[2.]}))

(7)內核

內核(kernel):能夠運行在特定設備(eg: CPU GPU)上的一種對操作的實現。

關鍵詞:計算模型、數據模型 和 運行模型;神經網絡的主要計算流程。

一、計算模型——計算圖

計算圖是 TF 中最基本的一個概念,TF 中所有計算都會被轉化為計算圖上的節點。

1、計算圖

TensorFlow

  • Tensor(張量): 一種數據結構,簡單理解為多維數組,數據流圖的邊。
  • Flow(流): 表示張量之間通過計算相互轉化的過程,數據流圖中節點所做的操作。

2、計算圖使用

TF 程序一般可以分為兩個階段:

  • 第一階段:定義計算圖中所有計算;
  • 第二階段:執行計算階段;

(1)計算階段

計算定義階段:

import tensorflow as tfa = tf.constant([1.0, 2.0], name="a") b = tf.constant([2.0, 3.0], name="b") result = a + b# a.graph可以查看張量所屬的計算圖,若沒特指,則為默認的計算圖 print(a.graph is tf.get_default_graph()) # True

在此過程中,TF 會自動將定義的計算轉化為計算圖上的節點。在 TF 程序中,系統會自動維護一個默認的計算圖,通過 tf.get_default_graph 函數可以獲得當前默認的計算圖。

通過 tf.Graph 生成新的計算圖,不同計算圖上的張量和運算不會共享,這樣可以隔離張量和計算。

import tensorflow as tf## tf.Graph 生成新的計算圖 g1 = tf.Graph() with g1.as_default():# 在計算圖 g1 中定義變量“v”,并初始化為 0.v = tf.get_variable("v", initializer=tf.zeros_initializer()(shape=[1]))g2 = tf.Graph() with g2.as_default():# 在計算圖 g2 中定義變量“v”,并初始化為 1.v = tf.get_variable("v", initializer=tf.ones_initializer()(shape=[1]))# 在計算圖 g1 中讀取變量 “v” 的取值。 with tf.Session(graph=g1) as sess:tf.global_variables_initializer().run()with tf.variable_scope("", reuse=True):print(sess.run(tf.get_variable("v")))# 在計算圖 g2 中讀取變量 “v” 的取值。 with tf.Session(graph=g2) as sess:tf.global_variables_initializer().run()with tf.variable_scope("", reuse=True):print(sess.run(tf.get_variable("v")))

輸出

2019-03-08 16:51:32.897748: I tensorflow/core/platform/cpu_feature_guard.cc:141] Your CPU supports instructions that this TensorFlow binary was not compiled to use: AVX2 FMA 2019-03-08 16:51:33.012464: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:897] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero 2019-03-08 16:51:33.013465: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1405] Found device 0 with properties: name: GeForce GTX 1060 major: 6 minor: 1 memoryClockRate(GHz): 1.6705 pciBusID: 0000:01:00.0 totalMemory: 5.94GiB freeMemory: 5.57GiB 2019-03-08 16:51:33.013487: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1484] Adding visible gpu devices: 0 2019-03-08 16:51:33.239035: I tensorflow/core/common_runtime/gpu/gpu_device.cc:965] Device interconnect StreamExecutor with strength 1 edge matrix: 2019-03-08 16:51:33.239077: I tensorflow/core/common_runtime/gpu/gpu_device.cc:971] 0 2019-03-08 16:51:33.239084: I tensorflow/core/common_runtime/gpu/gpu_device.cc:984] 0: N 2019-03-08 16:51:33.239567: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1097] Created TensorFlow device (/job:localhost/replica:0/task:0/device:GPU:0 with 5338 MB memory) -> physical GPU (device: 0, name: GeForce GTX 1060, pci bus id: 0000:01:00.0, compute capability: 6.1) [0.] 2019-03-08 16:51:33.305269: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1484] Adding visible gpu devices: 0 2019-03-08 16:51:33.305352: I tensorflow/core/common_runtime/gpu/gpu_device.cc:965] Device interconnect StreamExecutor with strength 1 edge matrix: 2019-03-08 16:51:33.305360: I tensorflow/core/common_runtime/gpu/gpu_device.cc:971] 0 2019-03-08 16:51:33.305365: I tensorflow/core/common_runtime/gpu/gpu_device.cc:984] 0: N 2019-03-08 16:51:33.305609: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1097] Created TensorFlow device (/job:localhost/replica:0/task:0/device:GPU:0 with 5338 MB memory) -> physical GPU (device: 0, name: GeForce GTX 1060, pci bus id: 0000:01:00.0, compute capability: 6.1) [1.]

(2)相關設置

通過 tf.Graph.device 函數可以指定運行計算的設備(GPU)。

g = tf.Graph() with g.device('/gpu:0'):result = a + b

在一個計算圖中,可以通過集合(collection)來管理不同類別的資源。eg:tf.add_to_collection 函數可將資源加入一個或多個集合中,然后通過 tf.get_collection 函數獲取一個集合里面所有的資源。資源包括:張量、變量或運行 tf 程序所需的隊列資源等等。

(3)TF 中維護的集合列表

集合名稱集合內容使用場景
tf.GraphKeys.VARIBLES所有變量持久化TF 模型
tf.GraphKeys.TRAINABLE_VARIABLES可學習的變量(NN中參數)模型訓練、生成模型可視化等內容
tf.GraphKeys.SUMMARIES日志生成相關的張量TF 計算可視化
tf.GraphKeys.QUEUE_RUNNERS處理輸入的 QueueRunner處理輸入
tf.GraphKeys.MOVING_AVERAGE_VARIABLES所有計算了滑動平均值的變量計算變量的滑動平均值

二、數據模型——張量

1、張量的概念

在 Tensorflow 中所有的數據都是通過張量的形式表示,簡單理解為 多維數組。但張量在 Tensorflow 中的實現不是直接采用數組的形式,張量只是對 TF 中運算結果的引用。在張量中沒有真正保存數字,而是何如得到這些數字的計算過程。

import tensorflow as tf# tf.constant 是一個計算,這個計算的結果是一個張量,保存在變量 a 中。 a = tf.constant([1.0, 2.0], name="a") b = tf.constant([2.0, 3.0], name="b") result = tf.add(a, b, name="add") print(result)

輸出

Tensor("add_2:0", shape=(2,), dtype=float32)

分析:
一個張量中主要保存三個屬性:名字(name)、維度(shape)和類型(type)。

  • name:一個張量的唯一標識符,同時給出這個張量是如何計算出來的。命名格式:node:src_output ,其中 node 為節點名稱,src_output 表示當前張量來自節點的第幾個輸出。add:0 表示result這個張量是計算節點 add 輸出的第一個結果。
  • shape:張量的維度。shape=(2,) 表示張量 result 是一個長度為 2 的一維數組。
  • type:張量的唯一類型。TF 對參與運算的張量進行類型檢查,若不匹配則會報錯。例如:
import tensorflow as tfa = tf.constant([1, 2], name="a") # 修改如下: # a = tf.constant([1, 2], name="a", dtype=tf.float32) b = tf.constant([2.0, 3.0], name="b") result = tf.add(a, b, name="add")

輸出

ValueError: Tensor conversion requested dtype int32 for Tensor with dtype float32: 'Tensor("b_1:0", shape=(2,), dtype=float32)'

通過指定 dtype 來明確指出變量或常量的類型。 TF 支持14種不同的類型,主要包括:實數( tf.float32、tf.float64 )、整數( tf.int8、tf.int16、tf.int32、tf.int64、tf.uint8 )、布爾型( tf.bool ) 和復數(tf.complex64、tf.complex128)

2、張量的使用

(1)對中間計算結果的引用

當一個計算包含很多中間結果時,使用張量可以大大提高代碼的可讀性。同時,可以方便獲取中間結果。

# 使用張量記錄中間結果 a = tf.constant([1.0, 2.0], name="a") b = tf.constant([2.0, 3.0], name="b") result = a + b# 直接計算向量的和 result = tf.constant([1.0, 2.0], name="a") + tf.constant([2.0, 3.0], name="b")

(2)獲取計算結果

當計算圖構造完成之后,張量可以用來獲取計算結果。
tf.Session().run(result) 獲取計算結果。

三、運行模型——會話(session)

使用會話(session)執行定義好的運算。
會話擁有并管理 TF 程序運行是所有的資源,在所有計算完成之后需要關閉會話來幫助系統回收資源,否則會出現資源泄露的問題。

1、兩種模式

  • 需要明確調用會話生成函數和關閉會話函數。該模式代碼流程如下:
# 創建會話 sess = tf.Session() # 例如獲取張量 result 的取值 sess.run(result) # 關閉會話使得本次運行中使用的資源可以被釋放 sess.close()

在這種模式下,在所有計算完成之后,需要明確調用 Session.close() 函數來關閉會話并釋放資源。但是,若程序異常退出則不會執行該函數。
==》

  • 通過 Python 的上下文管理器來使用會話。
# 創建一個會話,并通過 Python 的上下文管理器來使用會話 with tf.Session() as sess:sess.run(...) # 不再需要調用'Session.close()'函數關閉會話 # 當上下文退出時,會話關閉和資源釋放也自動完成

2、默認會話設置

TF 不會自動生成默認的會話,而是需要手動指定。當默認的會話被指定之后,可通過 tf.Tensor.eval 函數計算一個張量的值。

sess = tf.Session() with sess.as_default():print(result.eval())# 兩部分等價 sess = tf.Session() print(sess.run(result)) print(result.eval(session=sess))

tf.InteractiveSession() 函數可以自動將生成的會話注冊為默認會話。

sess = tf.InteractiveSession() print(result.eval()) sess.close()

3、配置會話

配置會話:通過 tf.ConfigProto 函數配置需要生成的會話。通過 tf.ConfigProto 可以配置類似并行的線程數、GPU分配策略、運算超時時間等參數。
最常用的參數:

  • allow_soft_placement:布爾型的參數,當它為 True 時,以下任意一個條件成立時,GPU上的運算可以放到CPU上進行:
    • 運算無法在 GPU 上執行;
    • 沒有GPU資源;
    • 運算輸入包含對CPU計算結果的引用。
  • log_device_placement:當它為 True 時,日志中將會記錄每個節點被安排在哪個設可備上以方便調試;實際中,設置為 False 可減少日志量
config = tf.ConfigProto(allow_soft_placement=True, log_device_placement=True) sess1 = tf.InteractiveSession(config=config) sess2 = tf.Session(config=config)

四、實現神經網絡

1、過程

使用神經網絡解決分類問題可分為以下步驟:
(1) 提取問題中實體的特征向量作為神經網絡的輸入;
(2) 定義網絡結構;
(3) 神經網絡的訓練(調整參數)
(4) 預測未知數據。

2、前向傳播算法


TF 程序

a = tf.matmul(x, w1) y = tf.matmul(a, w2)

3、神經網絡參數 與 TensorFlow變量

側重點: Tensorflow 是如何組織、保存及使用神經網絡中的參數。

(1)tf.Variable(變量)

作用:保存和更新神經網絡中的參數,需要進行初始化,還可設置為常數或通過其他變量的初始值計算得到。

神經網絡中,常見的初始化:隨機初始化。

## 聲明均值為0,標準差為2的2*3隨機數矩陣。mean參數可以設置平均值,默認為0 weights = tf.Variable(tf.random_normal([2, 3], stddev=2))

隨機數生成函數

函數名稱隨機數分布主要參數|
tf.random_normal正態分布均值、標準差、取值類型
tf.truncated_normal正態分布,若隨機生成的值偏離均值超過2個標準差,則重新生成均值、標準差、取值類型
tf.random_uniform均勻分布最大取值、最小取值、取值類型
tf.random_gammaGamma分布形狀參數alpha、尺度參數beta、取值類型

(2)常數初始化

函數名稱功能樣例|
tf.zeros生成全0數組tf.zeros([2, 3], int32)->[[0, 0, 0], [0, 0, 0]]
tf.ones生成全1數組tf.ones([2, 3], int32)->[[1, 1, 1], [1, 1, 1]]
tf.fill生成一個全部為給定數字的數組tf.fill([2, 3], 9)->[[9, 9, 9], [9, 9, 9]]
tf.constant生成一個給定值的常量tf.constant([1, 2, 3])->[1, 2, 3]

(3)偏置項(bias)設置

bias 通常使用常數來設置。

## 設置初值全為0且長度為3的變量 biases = tf.Variable(tf.zeros([3]))

(4)其他變量的初始值進行初始化

w2 = tf.Variable(weights.initialized_value()) w3 = tf.Variable(weights.initialized_value() * 2.0)

(5)樣例

import tensorflow as tf# 聲明w1、w2兩個變量。seed參數設置隨機種子,保證每次結果一致 w1 = tf.Variable(tf.random_normal((2, 3), stddev=1, seed=1)) w2 = tf.Variable(tf.random_normal((3, 1), stddev=1, seed=1))# 定義為一個1*2矩陣 x = tf.constant([[0.7, 0.9]])# 前向傳播 a = tf.matmul(x, w1) y = tf.matmul(a, w2)sess = tf.Session() sess.run(w1.initializer) # 初始化 w1 sess.run(w2.initializer) # 初始化 w2print(sess.run(y)) sess.close()

輸出

[[3.957578]]

通過 tf.global_variables_initalizer 函數實現初始化所有變量。

init_op = tf.global_variable_initalizer() sess.run(init_op)

4、通過Tensorflow訓練神經網絡模型

使用反向傳播算法訓練神經網絡的流程圖。

反向傳播算法實現了一個迭代過程。在每次迭代的開始,首先需要選取一小部分訓練數據(batch)。然后,這個 batch 的樣例會通過前向傳播算法得到神經網絡模型的預測結果,并計算與 ground truth 之間的差距。最后,反向傳播算法通過這個差距,更新神經網絡的參數,使得預測結果與 ground truth 更加接近。

5、完整代碼

import tensorflow as tf from numpy.random import RandomState# 設置訓練數據 batch 大小 batch_size = 8# 定義神經網絡參數 w1 = tf.Variable(tf.random_normal([2, 3], stddev=1, seed=1)) w2 = tf.Variable(tf.random_normal([3, 1], stddev=1, seed=1))# 訓練數據 x = tf.placeholder(tf.float32, shape=(None, 2), name='x-input') y_ = tf.placeholder(tf.float32, shape=(None, 1), name='y-input')# 前向傳播 a = tf.matmul(x, w1) y = tf.matmul(a, w2)# 損失函數和反向傳播過程 cross_entropy = -tf.reduce_mean(y_ * tf.log(tf.clip_by_value(y, 1e-10, 1.0))) train_step = tf.train.AdamOptimizer(0.001).minimize(cross_entropy)# 通過隨機數生成一個模擬數據集 rdm = RandomState(1) dataset_size = 120 X = rdm.rand(dataset_size, 2) Y = [[int(x1 + x2) < 1] for (x1, x2) in X]# 創建會話來運行 TF 程序 with tf.Session() as sess:init_op = tf.initialize_all_variables()# 初始化變量sess.run(init_op)print(sess.run(w1))print(sess.run(w2))# 設置訓練的輪數STEPS = 5000for i in range(STEPS):# 每次選取batch_size個樣本進行訓練start = (i * batch_size) % dataset_sizeend = min(start + batch_size, dataset_size)# 通過選取的樣本訓練神經網絡并更新參數sess.run(train_step, feed_dict={x: X[start: end], y_:Y[start: end]})if i % 1000 == 0:# 每隔一段時間計算所有數據上的交叉熵損失函數。total_cross_entropy = sess.run(cross_entropy, feed_dict={x: X, y_: Y})print("After %d training step(s), cross entropy on all data is %g" % (i, total_cross_entropy))print(sess.run(w1))print(sess.run(w2))

輸出

2019-03-09 12:22:31.932975: I tensorflow/core/platform/cpu_feature_guard.cc:141] Your CPU supports instructions that this TensorFlow binary was not compiled to use: AVX2 FMA 2019-03-09 12:22:32.053557: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:897] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero 2019-03-09 12:22:32.054556: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1405] Found device 0 with properties: name: GeForce GTX 1060 major: 6 minor: 1 memoryClockRate(GHz): 1.6705 pciBusID: 0000:01:00.0 totalMemory: 5.94GiB freeMemory: 5.49GiB 2019-03-09 12:22:32.054595: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1484] Adding visible gpu devices: 0 2019-03-09 12:22:32.284779: I tensorflow/core/common_runtime/gpu/gpu_device.cc:965] Device interconnect StreamExecutor with strength 1 edge matrix: 2019-03-09 12:22:32.284804: I tensorflow/core/common_runtime/gpu/gpu_device.cc:971] 0 2019-03-09 12:22:32.284810: I tensorflow/core/common_runtime/gpu/gpu_device.cc:984] 0: N 2019-03-09 12:22:32.285118: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1097] Created TensorFlow device (/job:localhost/replica:0/task:0/device:GPU:0 with 5257 MB memory) -> physical GPU (device: 0, name: GeForce GTX 1060, pci bus id: 0000:01:00.0, compute capability: 6.1) WARNING:tensorflow:From /usr/local/lib/python3.6/dist-packages/tensorflow/python/util/tf_should_use.py:118: initialize_all_variables (from tensorflow.python.ops.variables) is deprecated and will be removed after 2017-03-02. Instructions for updating: Use `tf.global_variables_initializer` instead. [[-0.8113182 1.4845988 0.06532937][-2.4427042 0.0992484 0.5912243 ]] [[-0.8113182 ][ 1.4845988 ][ 0.06532937]] After 0 training step(s), cross entropy on all data is 0.071992 After 1000 training step(s), cross entropy on all data is 0.0167723 After 2000 training step(s), cross entropy on all data is 0.00941592 After 3000 training step(s), cross entropy on all data is 0.00749986 After 4000 training step(s), cross entropy on all data is 0.00600073 [[-1.984426 2.6028152 1.7178398][-3.4925323 1.0919542 2.1556146]] [[-1.8463261][ 2.7097478][ 1.4506569]]

關鍵點

tf提供了 placeholder 機制用于提供輸入數據。 placeholder 相當于定義了一個位置,這個位置中的數據在程序運行時再指定。定義 placeholder 時,需要指定類型,但是不一定需要維度,因為可以根據提供的數據推導出來。

總結

以上是生活随笔為你收集整理的【TensorFlow】笔记1:入门笔记的全部內容,希望文章能夠幫你解決所遇到的問題。

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