本文目錄一覽:
python GIL 和 線程鎖是不是同一個東西
今天看到一篇文章,講述的是幾個提升python性能的項目:傳送門
在看的過程中,接觸到一個名詞,一個從學python開始就一直看到,但是從來都是一知半解的名詞,心裡不開心,必須把它搞明白,對了,這個詞就是 GIL。網上搜索了一些資料,粗淺的理解了什麼是GIL,自己感覺學習的過程比較好,感覺略有收穫,老規矩,為了鞏固知識,自己整片文章出來寫一寫,其實好多文章已經寫的很完善了,所以這篇隨筆,只做知識鞏固,如有雷同,請各位原創作者原諒,小菜鳥一枚,如果哪裡寫的有問題,還請各位前輩不吝指正。
一句話:解決多線程之間數據完整性和狀態同步的最簡單方法自然就是加鎖。
首先,GIL的全名,Global Interpreter Lock,鑒於英文水平,不做名詞翻譯,以免誤導。大體解釋一下,這個鎖就是用來為了解決Cpython多線程中線程不安全問題引入的一個全局排它鎖,它的作用就是在多線程情況下,保護共享資源,為了不讓多個線程同時操作共享資源,導致不可預期的結果而加上的鎖,在一個線程操作共享資源時,其他線程請求該資源,只能等待GIL解鎖。這個設置在Cpython剛引入多線程概念的時候就有了,然後後續的各種包和組件開發都不可避免的受到了GIL的影響,所以有人會說,python在多線程處理的時候很慢。python GIL實現方式類似於如下偽代碼:
if __name__ == ‘__main__’:
GIL鎖開始運作
主線程做操作
主線程完成操作
GIL鎖釋放資源
所以多線程共同操作共享資源的時候,有一個線程競得了資源,它就被GIL鎖保護起來,其他線程只能是在那裡等着,但是這個時候,線程的休眠喚醒,全部會消耗CPU資源,所以嘞,就會慢。
看到這個時候,我又發現了一個名詞:線程安全。這個名詞,也是那種特別熟悉,但就是無法清晰的說出它是啥的概念。查了資料,在這記一下:
線程安全就是多線程訪問時,採用了加鎖機制,當一個線程訪問該類的某個數據時,進行保護,其他線程不能進行訪問直到該線程讀取完,其他線程才可使用。不會出現數據不一致或者數據污染。 線程不安全就是不提供數據訪問保護,有可能出現多個線程先後更改數據造成所得到的數據是臟數據。
我自己想了一下,大約就是這樣,比如整個列表,倆個線程同時在列表中append操作,如果沒有鎖的保護,在機緣巧合之下,倆個線程同時先後申請了空間且沒來得及插入數據,然後這時列表中只會有一個空間,那麼在插入過程中只能有一個數據寫入,會造成不可知後果,有可能報錯終止,有可能有一個線程操作沒成功,那麼這個就是線程不安全了,大白話說,只要線程之間沒有共享資源,那麼就是線程安全的,有共享資源,為了保證線程安全,需要引進鎖的機制。
而後的文章中,有前輩做過實驗:
順序執行的單線程(single_thread.py)#! /usr/bin/pythonfrom threading import Threadimport timedef my_counter():
i = 0 for _ in range(100000000):
i = i + 1 return Truedef main():
thread_array = {}
start_time = time.time() for tid in range(2):
t = Thread(target=my_counter)
t.start()
t.join()
end_time = time.time() print(“Total time: {}”.format(end_time – start_time))if __name__ == ‘__main__’:
main()
同時執行的兩個並發線程(multi_thread.py)#! /usr/bin/pythonfrom threading import Threadimport timedef my_counter():
i = 0 for _ in range(100000000):
i = i + 1 return Truedef main():
thread_array = {}
start_time = time.time() for tid in range(2):
t = Thread(target=my_counter)
t.start()
thread_array[tid] = t for i in range(2):
thread_array[i].join()
end_time = time.time() print(“Total time: {}”.format(end_time – start_time))if __name__ == ‘__main__’:
main()
最終結果如下:
過程證明了因為GIL的存在,導致python在使用多線程的時候反而不如順序執行快。
此處我又溫習了一下python線程:
線程的順序執行還是多線程並發,取決於join函數的位置。join函數的作用是等待當前線程結束,所以每一個線程創建之後,調用start函數,這是在後面跟上該線程的join函數,那麼就是順序執行,如果多個線程先完成創建和start,最後加上join函數,那麼就變成了多線程並發。
這就是今天的學習內容,其實所有知識網上都能找到,更想分享的是一種學習的方法,一種本身很不推薦的學習方法,那就是類似於探索性測試的學習,啥不懂就去看啥,有些時候,我們學習東西確實不能非要究其內在,軟件行業的學習本身在非本行人事看來就特別神奇且枯燥,所以最初的學習,我們需要整個圖形界面,讓我們學到的東西有了成就感,如果上來先去研究機器碼,那麼沒幾個人願意學下去,但是不管怎樣,既然走上了軟件行業的道路,這種探索性,打破砂鍋問到底的學習,在我的感覺里應該是必經之路,也就是所謂的底層研究。以安卓開發舉例,如果做安卓開發的,雖然能寫出很漂亮的界面,解決所有的bug,如果不了解安卓系統linux層的知識,在我的眼裡,從未把這種研發看做大牛。當然我並不覺得不了解linux底層的安卓研發可以解決任何bug
當下的軟件行業進入了一個神奇的階段,我已經聽過無數遍的理論,培訓機構出來就能賺錢,大學讀着沒用,在這裡不討論教育體制問題,從個人情感上,我覺得大學教育雖然沒有教給學生直接找工作的技能,但是給了所有學生一個能夠了解基礎知識的園地,換而言之,作為行業的一員,總應該有將行業發展起來的覺悟,行業內整體風氣,缺乏靜下心來的沉澱。在大談敏捷,行為驅動,機器學習的同時,自己需要靜下心來回頭看看,基礎已然不牢,再走下去是否有些危險。是不是學習軟件技術,就是為了獲取互聯網行業那虛高的工資,是否已經局限於第三方框架,一旦框架出現問題,只能打給客服而束手無策,是否有過沒有做任何嘗試就將bug歸咎於安卓系統,阿里中間件等等,是不是舊技術還沒用明白,為了新技術就可以不再去研究。
還是小菜鳥,在此大談行業發展難免有些放肆,如有不對的地方,還請各位前輩不吝指正
怎麼用python寫tensorflow
開始使用
TensorFlow並不是一個純粹的神經網絡框架, 而是使用數據流圖進行數值分析的框架.
TensorFlow使用有向圖(graph)表示一個計算任務.圖的節點稱為ops(operations)表示對數據的處理,圖的邊flow 描述數據的流向.
該框架計算過程就是處理tensor組成的流. 這也是TensorFlow名稱的來源.
TensorFlow使用tensor表示數據. tensor意為張量即高維數組,在python中使用numpy.ndarray表示.
TensorFlow使用Session執行圖, 使用Variable維護狀態.tf.constant是只能輸出的ops, 常用作數據源.
下面我們構建一個只有兩個constant做輸入, 然後進行矩陣乘的簡單圖:
from tensorflow import Session, device, constant, matmul”’構建一個只有兩個constant做輸入, 然後進行矩陣乘的簡單圖:”’#如果不使用with session()語句, 需要手動執行session.close().
#with device設備指定了執行計算的設備:
# “/cpu:0”: 機器的 CPU.
# “/gpu:0”: 機器的第一個 GPU, 如果有的話.
# “/gpu:1”: 機器的第二個 GPU, 以此類推.
with Session() as session: # 創建執行圖的上下文
with device(‘/cpu:0’): # 指定運算設備
mat1 = constant([[3, 3]]) # 創建源節點
mat2 = constant([[2], [2]])
product = matmul(mat1, mat2) # 指定節點的前置節點, 創建圖
result = session.run(product) # 執行計算 print(result)123456789101112131415161718
下面使用Variable做一個計數器:
from tensorflow import Session, constant, Variable, add, assign, initialize_all_variables
state = Variable(0, name=’counter’) # 創建計數器one = constant(1) # 創建數據源: 1val = add(state, one) # 創建新值節點update = assign(state, val) # 更新計數器setup = initialize_all_variables() # 初始化Variablewith Session() as session:
session.run(setup) # 執行初始化
print(session.run(state)) # 輸出初值
for i in range(3):
session.run(update) # 執行更新
print(session.run(state)) # 輸出計數器值12345678910111213
在使用變量前必須運行initialize_all_variables()返回的圖, 運行Variable節點將返回變量的值.
本示例中將構建圖的過程寫在了上下文之外, 而且沒有指定運行設備.
上面示例中session.run只接受一個op作為參數, 實際上run可以接受op列表作為輸入:
session.run([op1, op2])1
上述示例一直使用constant作為數據源, feed可以在運行時動態地輸入數據:
from tensorflow import Session, placeholder, mul, float32
input1 = placeholder(float32)
input2 = placeholder(float32)
output = mul(input1, input2)with Session() as session: print session.run(output, feed_dict={input1: [3], input2: [2]})1234567
實現一個簡單神經網絡
神經網絡是應用廣泛的機器學習模型, 關於神經網絡的原理可以參見這篇隨筆, 或者在tensorflow playground上體驗一下在線demo.
首先定義一個BPNeuralNetwork類:
class BPNeuralNetwork:
def __init__(self):
self.session = tf.Session()
self.input_layer = None
self.label_layer = None
self.loss = None
self.trainer = None
self.layers = [] def __del__(self):
self.session.close()1234567891011
編寫一個生成單層神經網絡函數,每層神經元用一個數據流圖表示.使用一個Variable矩陣表示與前置神經元的連接權重, 另一個Variable向量表示偏置值, 並為該層設置一個激勵函數.
def make_layer(inputs, in_size, out_size, activate=None):
weights = tf.Variable(tf.random_normal([in_size, out_size]))
basis = tf.Variable(tf.zeros([1, out_size]) + 0.1)
result = tf.matmul(inputs, weights) + basis if activate is None: return result else: return activate(result)12345678
使用placeholder作為輸入層.
self.input_layer = tf.placeholder(tf.float32, [None, 2])1
placeholder的第二個參數為張量的形狀, [None, 1]表示行數不限, 列數為1的二維數組, 含義與numpy.array.shape相同.這裡, self.input_layer被定義為接受二維輸入的輸入層.
同樣使用placeholder表示訓練數據的標籤:
self.label_layer = tf.placeholder(tf.float32, [None, 1])1
使用make_layer為神經網絡定義兩個隱含層, 並用最後一層作為輸出層:
self.loss = tf.reduce_mean(tf.reduce_sum(tf.square((self.label_layer – self.layers[1])), reduction_indices=[1]))1
tf.train提供了一些優化器, 可以用來訓練神經網絡.以損失函數最小化為目標:
self.trainer = tf.train.GradientDescentOptimizer(learn_rate).minimize(self.loss)1
使用Session運行神經網絡模型:
initer = tf.initialize_all_variables()# do trainingself.session.run(initer)
for i in range(limit):
self.session.run(self.trainer, feed_dict={self.input_layer: cases, self.label_layer: labels})12345
使用訓練好的模型進行預測:
self.session.run(self.layers[-1], feed_dict={self.input_layer: case})1
完整代碼:
import tensorflow as tfimport numpy as npdef make_layer(inputs, in_size, out_size, activate=None):
weights = tf.Variable(tf.random_normal([in_size, out_size]))
basis = tf.Variable(tf.zeros([1, out_size]) + 0.1)
result = tf.matmul(inputs, weights) + basis if activate is None: return result else: return activate(result)class BPNeuralNetwork:
def __init__(self):
self.session = tf.Session()
self.input_layer = None
self.label_layer = None
self.loss = None
self.optimizer = None
self.layers = [] def __del__(self):
self.session.close() def train(self, cases, labels, limit=100, learn_rate=0.05):
# 構建網絡
self.input_layer = tf.placeholder(tf.float32, [None, 2])
self.label_layer = tf.placeholder(tf.float32, [None, 1])
self.layers.append(make_layer(self.input_layer, 2, 10, activate=tf.nn.relu))
self.layers.append(make_layer(self.layers[0], 10, 2, activate=None))
self.loss = tf.reduce_mean(tf.reduce_sum(tf.square((self.label_layer – self.layers[1])), reduction_indices=[1]))
self.optimizer = tf.train.GradientDescentOptimizer(learn_rate).minimize(self.loss)
initer = tf.initialize_all_variables() # 做訓練
self.session.run(initer) for i in range(limit):
self.session.run(self.optimizer, feed_dict={self.input_layer: cases, self.label_layer: labels}) def predict(self, case):
return self.session.run(self.layers[-1], feed_dict={self.input_layer: case}) def test(self):
x_data = np.array([[0, 0], [0, 1], [1, 0], [1, 1]])
y_data = np.array([[0, 1, 1, 0]]).transpose()
test_data = np.array([[0, 1]])
self.train(x_data, y_data)
print(self.predict(test_data))
nn = BPNeuralNetwork()
nn.test()12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152
上述模型雖然簡單但是使用不靈活, 作者採用同樣的思想實現了一個可以自定義輸入輸出維數以及多層隱含神經元的網絡, 可以參見dynamic_bpnn.py
import tensorflow as tfimport numpy as npdef make_layer(inputs, in_size, out_size, activate=None):
weights = tf.Variable(tf.random_normal([in_size, out_size]))
basis = tf.Variable(tf.zeros([1, out_size]) + 0.1)
result = tf.matmul(inputs, weights) + basis if activate is None: return result else: return activate(result)class BPNeuralNetwork:
def __init__(self):
self.session = tf.Session()
self.loss = None
self.optimizer = None
self.input_n = 0
self.hidden_n = 0
self.hidden_size = []
self.output_n = 0
self.input_layer = None
self.hidden_layers = []
self.output_layer = None
self.label_layer = None
def __del__(self):
self.session.close() def setup(self, ni, nh, no):
# 設置參數個數
self.input_n = ni
self.hidden_n = len(nh) #隱藏層的數量
self.hidden_size = nh #每個隱藏層中的單元格數
self.output_n = no #構建輸入層
self.input_layer = tf.placeholder(tf.float32, [None, self.input_n]) #構建標籤層
self.label_layer = tf.placeholder(tf.float32, [None, self.output_n]) #構建隱藏層
in_size = self.input_n
out_size = self.hidden_size[0]
inputs = self.input_layer
self.hidden_layers.append(make_layer(inputs, in_size, out_size, activate=tf.nn.relu)) for i in range(self.hidden_n-1):
in_size = out_size
out_size = self.hidden_size[i+1]
inputs = self.hidden_layers[-1]
self.hidden_layers.append(make_layer(inputs, in_size, out_size, activate=tf.nn.relu)) #構建輸出層
self.output_layer = make_layer(self.hidden_layers[-1], self.hidden_size[-1], self.output_n) def train(self, cases, labels, limit=100, learn_rate=0.05):
self.loss = tf.reduce_mean(tf.reduce_sum(tf.square((self.label_layer – self.output_layer)), reduction_indices=[1]))
self.optimizer = tf.train.GradientDescentOptimizer(learn_rate).minimize(self.loss)
initer = tf.initialize_all_variables() #做訓練
self.session.run(initer) for i in range(limit):
self.session.run(self.optimizer, feed_dict={self.input_layer: cases, self.label_layer: labels}) def predict(self, case):
return self.session.run(self.output_layer, feed_dict={self.input_layer: case}) def test(self):
x_data = np.array([[0, 0], [0, 1], [1, 0], [1, 1]])
y_data = np.array([[0, 1, 1, 0]]).transpose()
test_data = np.array([[0, 1]])
self.setup(2, [10, 5], 1)
self.train(x_data, y_data)
print(self.predict(test_data))
nn = BPNeuralNetwork()
nn.test()12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576
python能做什麼
python的用途:
Python的優勢有必要作為第一步去了解,Python作為面向對象的腳本語言,優勢就是數據處理和挖掘,這也註定了它和AI、互聯網技術的緊密聯繫。
網絡爬蟲。顧名思義,從互聯網上爬取信息的腳本,主要由urllib、requests等庫編寫,實用性很強,小編就曾寫過爬取5w數據量的爬蟲。在大數據風靡的時代,爬蟲絕對是新秀。
人工智能。AI使Python一戰成名,AI的實現可以通過tensorflow庫。神經網絡的核心在於激活函數、損失函數和數據,數據可以通過爬蟲獲得。訓練時大量的數據運算又是Python的show time。
擴展資料:
Python開發人員盡量避開不成熟或者不重要的優化。一些針對非重要部位的加快運行速度的補丁通常不會被合併到Python內。在某些對運行速度要求很高的情況,Python設計師傾向於使用JIT技術,或者用使用C/C++語言改寫這部分程序。可用的JIT技術是PyPy。
Python是完全面向對象的語言。函數、模塊、數字、字符串都是對象。並且完全支持繼承、重載、派生、多繼承,有益於增強源代碼的復用性。
Python支持重載運算符和動態類型。相對於Lisp這種傳統的函數式編程語言,Python對函數式設計只提供了有限的支持。有兩個標準庫(functools, itertools)提供了Haskell和Standard ML中久經考驗的函數式程序設計工具。
參考資料來源:百度百科-Python
原創文章,作者:簡單一點,如若轉載,請註明出處:https://www.506064.com/zh-hant/n/130659.html