python 神經網絡工具_神經網絡15分鐘入門!使用python從零開始寫一個兩層神經網絡...

本篇是該系列的第三篇,建議在閱讀本篇文章之前先看前兩篇文章。

在本文中將使用python實現之前描述的兩層神經網絡,并完成所提出的“象限分類”的問題。

需要注意的是,雖然標題叫做神經網絡15分鐘入門,但是到這篇文章,對于沒接觸過python的同學,15分鐘怕是不太夠。好在python本身不算太難,如果你有其他語言的基礎,結合本文盡量詳細的講解,對于算法層面的理解應該還是可以做到的。如果還是不能理解,建議先入門python再來看本文,畢竟想做深度學習,對語言的掌握是基本要求。

另外,這篇文章的正確食用方法是將代碼搞到自己的電腦上,然后單步調試逐行看參數的變化,如有不明白的地方再對照文章中的講解來理解。單純靠看文章是不太容易融會貫通的(可以腦內debug的同學可以忽略)。

一、運行環境

運行環境:Python 3.6.5+Anaconda3+VS Code

Anaconda是一個環境管理器,安裝Anaconda,就相當于安裝了python+各種工具包,這些工具包在我們進行神經網絡的應用時十分必要。

VS Code是微軟的免費代碼編輯器,功能相當強大,插件相當豐富,界面非常美觀。當然,你也可以用pycharm或者eclipse,看個人習慣。

運行環境的搭建就不詳細介紹了,在網上能找到很多教程,如果有疑問可以留言,需要的話我會寫一篇番外對環境搭建進行詳細講述。

二、編程實現

1.導入numpy包

import numpy as np

#numpy是一個強大的數學工具包

#我們后邊要用到的是numpy中的數組類型、矩陣運算等

#不明白沒關系,用到的時候會再解釋

2.前向傳播函數

# 前向傳播函數

# - x:包含輸入數據的numpy數組,形狀為(N,d_1,...,d_k)

# - w:形狀為(D,M)的一系列權重

# - b:偏置,形狀為(M,)

def affine_forward(x, w, b):

out = None # 初始化返回值為None

N = x.shape[0] # 重置輸入參數X的形狀

x_row = x.reshape(N, -1) # (N,D)

out = np.dot(x_row, w) + b # (N,M)

cache = (x, w, b) # 緩存值,反向傳播時使用

return out,cache

這一段程序是定義了了一個名為affine_forward的函數,其功能就是計算這個公式(仿射):如果不記得這個公式了就回去看一下第一篇文章

這個函數的輸入參數就是公式中的矩陣X,W1和b1,對應到程序中就是x,w和b。

不過需要注意的是,程序中的輸入參數x,其形狀可以是(N,d_1,...,d_k),這是什么意思呢?在我們這個例子中,輸入參數x是:

[2,1],

[-1,1],

[-1,-1],

[1,-1]]

它是一個4行2列的二維數組,那么x的形狀就是(4,2),對應的參數N=4,d_1=2。這是我們用來做訓練的坐標數據,分別對應了I、II、III、IV象限。

在某些應用場景中,x的維度可能更高。比如對于一個20*20像素的4張灰度圖,x的形狀將是(4,20,20),對應的參數就是N=4,d_1=20,d_2=20。(這里邊第一個參數用N表示,它代表的是同時用于計算前向傳播的數據有幾組,后邊的參數d_1~d_k代表的是數據本身的形狀。)

對于這種維度大于2的x來說,需要對其進行重新塑形,也就是將(4,20,20)的高維數組變化為(4,20*20)這樣的二位數組。

為什么要這么做呢?是為了方便計算。這樣變換之后高維的向量被“拍扁”成一維向量(長度為20*20的一維向量),對應的W和b也都是一維的,既統一了參數形式,又不會影響數據的正常使用。

這個“拍扁”的動作,是用上述代碼中的這兩行完成的:

N = x.shape[0] # 重置輸入參數X的形狀

x_row = x.reshape(N,-1) # (N,D)

x.shape[0]是獲取數組x的第0維長度,也就是數據的組數,對于上述的4行2列的數組,其值為4;對于上述(4,20,20)的數組,其值也為4.

x.reshape(N,-1)是對x重新塑形,即保留第0維,其他維度排列成1維。對于形狀為(4,2)的數組,其形狀不變,對于形狀為(4,20,20)的數組,形狀變為(4,20*20)。以此類推。

在完成reshape后,就可以進行矩陣的線性運算了:

out = np.dot(x_row, w)+ b # (N,M)

.dot就是numpy中的函數,可以實現x_row與w的矩陣相乘。x_row的形狀為(N,D),w的形狀為(D,M),得到的out的形狀是(N,M)。

cache =(x, w, b) # 緩存值,反向傳播時使用

上面這句是將當前x,w和b的值緩存下來,留作反向傳播時使用。

3.反向傳播函數

# 反向傳播函數

# - x:包含輸入數據的numpy數組,形狀為(N,d_1,...,d_k)

# - w:形狀(D,M)的一系列權重

# - b:偏置,形狀為(M,)

def affine_backward(dout, cache):

x, w, b = cache # 讀取緩存

dx, dw, db = None, None, None # 返回值初始化

dx = np.dot(dout, w.T) # (N,D)

dx = np.reshape(dx, x.shape) # (N,d1,...,d_k)

x_row = x.reshape(x.shape[0], -1) # (N,D)

dw = np.dot(x_row.T, dout) # (D,M)

db = np.sum(dout, axis=0, keepdims=True) # (1,M)

return dx, dw, db

這一段是實現計算仿射層的反向傳播的函數。這篇文章的2.3節講的就是這段代碼的原理,如果不清楚可以先出門左轉看一下。

函數中第一句就是讀取緩存的x,w和b的值,為什么要這樣做呢?仿射變換反向傳播的最重要的3個目的,分別是:①更新參數w的值②計算流向下一個節點的數值③更新參數b的值。“更新”的時候需要“舊”值,也就是緩存值,具體操作如下:

①為了得到w的值,要將上一節點輸入的值(dout)乘以x。

dw = np.dot(x_row.T, dout) # (D,M)

②為了得到流入下一個節點的值(x),要將上一節點的輸入值(dout)乘以w。你可能發現了,①中為了得到w是乘以的x,②中為了得到x是乘以的w,也就是將系數交叉相乘了。

dx = np.dot(dout, w.T) # (N,D)

③為了得到b,只需要將out直接傳過來就可以,為了保持維度一致,這里將out求和。

db = np.sum(dout, axis=0, keepdims=True) # (1,M)

在仿射變換反向傳播這里,各種矩陣的維度可能會讓你感到困惑。這里的維度包含三個,分別是D、M和N。

看一下下圖,其中包括兩個仿射變換,我們以第一個舉例,其變換公式為H=X*W1+b1。該仿射變換對應到程序中的D的值為2,M的值為50,N的值為4。怎么理解呢?X的維度就是N*D,而M的值就是W1的第二個維度,這里記住就好了,每個仿射變換都是這樣的(其實不記住也沒關系,這里沒有什么物理含義,就是單純的矩陣變換的維度而已。這幾個維度在反向傳播時可能難理解,這是數學公式推導來的,迷惑的時候找出這篇文章過來看一遍就明白了)。注意看矩陣維度

4.參數初始化

X = np.array([[2,1],

[-1,1],

[-1,-1],

[1,-1]]) # 用于訓練的坐標,對應的是I、II、III、IV象限

t = np.array([0,1,2,3]) # 標簽,對應的是I、II、III、IV象限

np.random.seed(1) # 有這行語句,你們生成的隨機數就和我一樣了

# 一些初始化參數

input_dim = X.shape[1] # 輸入參數的維度,此處為2,即每個坐標用兩個數表示

num_classes = t.shape[0] # 輸出參數的維度,此處為4,即最終分為四個象限

hidden_dim = 50 # 隱藏層維度,為可調參數

reg = 0.001 # 正則化強度,為可調參數

epsilon = 0.001 # 梯度下降的學習率,為可調參數

# 初始化W1,W2,b1,b2

W1 = np.random.randn(input_dim, hidden_dim) # (2,50)

W2 = np.random.randn(hidden_dim, num_classes) # (50,4)

b1 = np.zeros((1, hidden_dim)) # (1,50)

b2 = np.zeros((1, num_classes)) # (1,4)

這一段程序對一些必要的參數進行了初始化,程序較為簡單,看注釋即可,不再詳細解釋。

對于訓練數據以及訓練模型已經確定的網絡來說,為了得到更好的訓練效果需要調節的參數就是上述的隱藏層維度、正則化強度和梯度下降的學習率,以及下一節中的訓練循環次數。

5.訓練與迭代

for j in range(10000): #這里設置了訓練的循環次數為10000

# ①前向傳播

H,fc_cache = affine_forward(X,W1,b1) # 第一層前向傳播

H = np.maximum(0, H) # 激活

relu_cache = H # 緩存第一層激活后的結果

Y,cachey = affine_forward(H,W2,b2) # 第二層前向傳播

# ②Softmax層計算

probs = np.exp(Y - np.max(Y, axis=1, keepdims=True))

probs /= np.sum(probs, axis=1, keepdims=True) # Softmax算法實現

# ③計算loss值

N = Y.shape[0] # 值為4

print(probs[np.arange(N), t]) # 打印各個數據的正確解標簽對應的神經網絡的輸出

loss = -np.sum(np.log(probs[np.arange(N), t])) / N # 計算loss

print(loss) # 打印loss

# ④反向傳播

dx = probs.copy() # 以Softmax輸出結果作為反向輸出的起點

dx[np.arange(N), t] -= 1 #

dx /= N # 到這里是反向傳播到softmax前

dh1, dW2, db2 = affine_backward(dx, cachey) # 反向傳播至第二層前

dh1[relu_cache <= 0] = 0 # 反向傳播至激活層前

dX, dW1, db1 = affine_backward(dh1, fc_cache) # 反向傳播至第一層前

# ⑤參數更新

dW2 += reg * W2

dW1 += reg * W1

W2 += -epsilon * dW2

b2 += -epsilon * db2

W1 += -epsilon * dW1

b1 += -epsilon * db1

這段程序是網絡訓練的核心,我將按照①前向傳播②Softmax層③計算loss值④反向傳播⑤參數更新這五個小結的順序依次講解:

①前向傳播

# ①前向傳播

H,fc_cache = affine_forward(X,W1,b1) # 第一層前向傳播

H = np.maximum(0, H) # 激活

relu_cache = H # 緩存第一層激活后的結果

Y,cachey = affine_forward(H,W2,b2) # 第二層前向傳播

第一句H,fc_cache = affine_forward(X,W1,b1) 調用了之前寫的前向傳播的函數,完成了第一層網絡的矩陣線性代數運算。

第二句H = np.maximum(0, H)是從0和H中選擇較大的值賦給H,也就是實現了ReLU激活層函數。

第四句Y,cachey = affine_forward(H,W2,b2),完成了第二層網絡的矩陣線性代數運算。

②Softmax層計算

# ②Softmax層計算

probs = np.exp(Y - np.max(Y, axis=1, keepdims=True))

probs /= np.sum(probs, axis=1, keepdims=True) # Softmax算法實現

這兩行是為了實現Softmax層的計算,在之前我們說過,Softmax的計算公式是:

不過在實際應用中會存在一個問題,比如i的值等于1000時,e^1000在計算機中會變成無窮大的inf,后續計算將無法完成,所以程序中會對計算公式做一些修改,實際使用的公式為:

在指數上減去常數C不影響最終結果(證明略),而這個常數C通常取i中的最大值。

第一句probs = np.exp(Y - np.max(Y, axis=1, keepdims=True)) 就是求輸出各個行的指數值,舉個例子,Y的值如果是:

[[-4,17,20,-4],

[10,-2,5,3],

[-5,3,4,10],

[-5,5,5,2]]

np.max(Y, axis=1, keepdims=True)計算得到的是[[20],[10],[10],[5]],后邊括號里的參數axis代表以豎軸為基準 ,在同行中取值; keepdims=True代表保持矩陣的二維特性。

所以np.exp(Y - np.max(Y, axis=1, keepdims=True)) 代表:Y矩陣中每個值減掉改行最大值后再取對數。

第二句probs /= np.sum(probs, axis=1, keepdims=True) 以行為單位求出各個數值對應的比例。也就是最終實現了Softmax層的輸出。

③計算loss值

# ③計算loss值

N = Y.shape[0] # 值為4

print(probs[np.arange(N), t]) # 打印各個數據的正確解標簽對應的神經網絡的輸出

loss = -np.sum(np.log(probs[np.arange(N), t])) / N # 計算loss

復習一下:交叉熵損失的求法是求對數的負數。

第一句N = Y.shape[0]取了最終輸出的維度,這個例子中為4,即四個象限。

第二句打印各個數據的正確解標簽對應的神經網絡的輸出。

其中probs[np.arange(N), t]講解一下:

N為4時,np.arange(N)會生成一個Numpy數組[0,1,2,3]。t中標簽是以[0,1,2,3]的形式儲存的,所以probs[np.arange(N), t]能抽出各個數據的正確解標簽對應的神經網絡輸出,在這個例子中,probs[np.arange(N), t]會成成numpy數組[probs[0,0], probs[1,1], probs[2,2], probs[3,3]]。

第三句loss = -np.sum(np.log(probs[np.arange(N), t])) / N中先求了N維數據中的交叉熵損失,然后對這N個交叉熵損失求平均值,作為最終loss值。

④反向傳播

# ④反向傳播

dx = probs.copy() # 以Softmax輸出結果作為反向輸出的起點

dx[np.arange(N), t] -= 1 #

dx /= N # 到這里是反向傳播到softmax前

dh1, dW2, db2 = affine_backward(dx, cachey) # 反向傳播至第二層前

dh1[relu_cache <= 0] = 0 # 反向傳播至激活層前

dX, dW1, db1 = affine_backward(dh1, fc_cache) # 反向傳播至第一層前

反向傳播計算是從Softmax層的輸出開始的。你是不是想問為什么不是從loss值開始算?

回看上一篇文章的2.5節,你會發現Softmax-with-Loss層的反向傳播結果計算,本身就是與loss無關的。而只與Softmax層輸出結果和教師標簽有關。換句話說,即使是從loss開始計算反向傳播,經過一系列化簡之后,這個loss值也會被化簡掉,化簡后的結果只包括Softmax層的輸出和教師標簽。

第一句代碼很簡單,就是將Softmax的輸出值賦給dx, 這里dx代表反向傳播的主線值。dx[np.arange(N), t]-=1這句代碼

第二句代碼是實現上一篇文章中y-t的操作(y就是Softmax層的輸出)。dx[np.arange(N), t]-=1這句代碼中,dx是一個4*4的數組,而t是一個內容為[0,1,2,3]的數組(見其初始化),N的值為4。np.arrange(N)會生成一個從0到3的數組[0,1,2,3],因為t中的標簽是以[0,1,2,3]的形式存儲的,所以dx[np.arange(N), t]能抽出各個數據的正確解標簽對應的神經網絡的輸出。在這個例子中dx[np.arange(N), t]會成成NumPy數組[dx[0,0],dx[1,1],dx[2,2],dx[3,3]。

第四、六句試一次仿射變幻的反向傳播,上邊說過了,不在具體解釋了。

第五句是ReLU激活層的反向傳播,至于為什么這樣寫,也去看上一篇文章吧~

⑤參數更新

# ⑤參數更新

dW2 += reg * W2

dW1 += reg * W1

W2 += -epsilon * dW2

b2 += -epsilon * db2

W1 += -epsilon * dW1

b1 += -epsilon * db1

前兩行是引入正則化懲罰項更新dW,后四行是引入學習率更新W和b。這部分理解起來比較簡單,如果有疑問可以參考上篇文章的第3節。

6.驗證

test = np.array([[2,2],[-2,2],[-2,-2],[2,-2]])

H,fc_cache = affine_forward(test,W1,b1) #仿射

H = np.maximum(0, H) #激活

relu_cache = H

Y,cachey = affine_forward(H,W2,b2) #仿射

# Softmax

probs = np.exp(Y - np.max(Y, axis=1, keepdims=True))

probs /= np.sum(probs, axis=1, keepdims=True) # Softmax

print(probs)

for k in range(4):

print(test[k,:],"所在的象限為",np.argmax(probs[k,:])+1)

給出了一組數據test,對已經訓練好的網絡進行驗證。

其實驗證的方法和訓練時的正向傳播的過程基本一致,即第一層網絡線性計算→激活→第二層網絡線性計算→Softmax→得到分類結果。

這部分代碼在之前也大多講過,不再詳述。

三、運行結果

在運行10000次迭代后,loss值以肉眼可見的速度下降。

最終loss值為:0.0040015

最終輸出結果為:

可見分類正確。

四、總結

本例是一個很簡單的神經網絡的例子,我們只用了一組數據用來訓練,其訓練結果應該是比較勉強的。之所以最終效果還行,只是我們選擇驗證的例子比較合適。要想得到比較完美的模型,需要有大量的、分散的訓練數據,比如第一象限不僅要有[1,1]這種數據,還要有[1000,1],[1,1000]這種,這里就不再詳述了。

“神經網絡15分鐘入門”系列到這里就結束啦。如果這三篇文章里的內容能夠融會貫通,相信對你后邊學習深度學習會有一些幫助。在神經網絡學習過程中能遇到的難點和坑我盡量都點出來了,如果還有什么疑問請留言給我吧,也許會出一篇番外集中回答。

參考:

《深度學習入門:基于Python的理論與實現》

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/539009.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/539009.shtml
英文地址,請注明出處:http://en.pswp.cn/news/539009.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

12.3目錄結構

目錄結構 設計好目錄結構 可讀性高可維護性高比如一個Foo項目Foo/--- bin/--- foo--- foo/--- tests/--- _init__.py--- test_main.py--- init.py--- main.py---doc--- conf.py---abc.rst--- setup.py--- requirement.txt--- README簡要解釋一下&#xff1a; bin/:存放項目的一…

pyecharts添加文字_超燃的文字云效果,用Python就能輕松get!

本文轉載自公眾號&#xff1a;數據森麟(ID&#xff1a;shujusenlin)作者&#xff1a;葉庭云鏈接&#xff1a;https://blog.csdn.net/fyfugoyfa/ 01 / 詞云圖詞云圖是一種用來展現高頻關鍵詞的可視化表達&#xff0c;通過文字、色彩、圖形的搭配&#xff0c;產生有沖擊力地視覺效…

同步關鍵詞synchronized

概述 synchronized是java中的一個關鍵字&#xff0c;也就是說是Java語言內置的特性。 synchronized( 一個任意的對象&#xff08;鎖&#xff09; ){ 代碼塊中放操作共享數據的代碼。 } public synchronized int getIndex() {return 1;}public static synchronized int getN…

python連接mysql用哪個模塊_Python連接MySQL數據庫之pymysql模塊使用

Python3連接MySQL本文介紹Python3連接MySQL的第三方庫--PyMySQL的基本使用。PyMySQL介紹PyMySQL 是在 Python3.x 版本中用于連接 MySQL 服務器的一個庫&#xff0c;Python2中則使用mysqldb。Django中也可以使用PyMySQL連接MySQL數據庫。PyMySQL安裝pip install pymysql連接數據…

mysql 創建視圖 主鍵_MySQL數據庫基礎操作命令,本文助你更上一層樓!

今天介紹的是關于Mysql數據庫一些操作的基礎命令用戶與權限創建用戶mysql>create user test identified by BaC321#; 修改密碼5.5版本及以前的命令mysql>set password for testpassowrd(!1A2#3); 5.6及以上命令mysql>update mysql.user set authentication_stringpass…

mysql 聚合函數 怎么用在條件里_MySql 中聚合函數增加條件表達式的方法

Mysql 與聚合函數在一起時候where條件和having條件的過濾時機where 在聚合之前過濾當一個查詢包含了聚合函數及where條件&#xff0c;像這樣的情況select max(cid) from t where t.id<999這時候會先進行過濾&#xff0c;然后再聚合。先過濾出ID《999的記錄&#xff0c;再查找…

drbd(三):drbd的狀態說明

1.幾種獲取狀態信息的方法 drbd有很多獲取信息的方式。在drbd84和之前的版本&#xff0c;大多都使用cat /proc/drbd來獲取信息&#xff0c;多數情況下&#xff0c;這個文件展示的信息對于管理和維護drbd來說已經足夠。 例如以下是drbd84上兩個volume的節點狀態信息&#xff1a;…

Lock的lock()方法

ReentrantLock是JDK唯一實現了Lock接口的類 lock() 是平常使用得最多的一個方法&#xff0c;就是用來獲取鎖。如果鎖已被其他線程獲取&#xff0c;則進行等待。 由于在前面講到如果采用Lock&#xff0c;必須主動去釋放鎖&#xff0c;并且在發生異常時&#xff0c;不會自動釋放鎖…

Lock的tryLock()方法

概述 tryLock()方法是有返回值的&#xff0c;它表示用來嘗試獲取鎖&#xff0c;如果獲取成功&#xff0c;則返回true&#xff0c;如果獲取失敗&#xff08;即鎖已被其他線程獲取&#xff09;&#xff0c;則返回false&#xff0c;這個方法無論如何都會立即返回。在拿不到鎖時不…

python requests庫詳解_python的requests庫詳解

快速上手迫不及待了嗎&#xff1f;本頁內容為如何入門 Requests 提供了很好的指引。其假設你已經安裝了 Requests。如果還沒有&#xff0c;去安裝一節看看吧。首先&#xff0c;確認一下&#xff1a;Requests 已安裝Requests 是最新的讓我們從一些簡單的示例開始吧。發送請求使用…

python QTreeWidgetItem下面有幾個子tree_python-nlp ch1筆記:nlp的基礎應用、高級應用、python優勢、nltk環境搭建...

本帖是對(印度)Jalaj Thanaki作品《python自然語言處理》的翻譯、縮減及改編~nlp的基礎應用NLP是AI的子分支&#xff0c;其相關概念可以用于以下專家系統中&#xff1a;語音識別系統問答系統機器翻譯文本摘要情感分析基于模板的聊天機器人文本分類主題分割nlp的高級應用理解自然…

C#使用ListView更新數據出現閃爍解決辦法

C#使用ListView更新數據出現閃爍解決辦法 在使用vs自動控件ListView控件時候&#xff0c;更新里面的部分代碼時候出現閃爍的情況 如圖&#xff1a; 解決以后&#xff1a; 解決辦法使用雙緩沖&#xff1a;添加新類繼承ListView 對其重寫 1 public class DoubleBufferListView : …

Lock的tryLock(long time, TimeUnit unit)方法

概述 tryLock(long time, TimeUnit unit)方法和tryLock()方法是類似的&#xff0c;只不過區別在于這個方法在拿不到鎖時會等待一定的時間&#xff0c;在時間期限之內如果還拿不到鎖&#xff0c;就返回false。如果一開始拿到鎖或者在等待期間內拿到了鎖&#xff0c;則返回true。…

python語音識別的第三方庫_python標準庫+內置函數+第三方庫: 7.音頻處理

python標準庫內置函數第三方庫 欲善其事&#xff0c;必先利其器 這其器必是python的標準庫內置函數&#xff0c;話說許多第三方庫&#xff0c; 也是對標準庫的使用&#xff0c;進行封裝&#xff0c;使得使用起來更方便。 這些庫以使用場景來分類: 7、音頻處理 音頻處理主要適用…

SperingBoot+vue文件上傳下載預覽

上傳文件&#xff1a; 前端&#xff1a; 整個過程&#xff0c;就是在使用FormData 添加 上File&#xff08;這個Blob&#xff09;&#xff0c;并且key要和后臺的名字對應上在點擊上傳按鈕開始上傳之前&#xff0c;使用了URL.createObjectURL(File)創建blobUrl&#xff0c;給了…

keepalived腦裂問題查找

在自己環境做keepalivedredis實驗時&#xff0c;當重啟了備用redies機器后&#xff0c;發現兩臺redies主機都拿到了VIP [plain] view plaincopy [rootredis2 ~]# ip addr list 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN link/loopbac…

python 多線程并行 矩陣乘法_python實現簡單的并行矩陣乘法

python實現簡單的并行矩陣乘法python實現簡單的并行矩陣乘法本文采用的矩陣乘法方式是利用一個矩陣的行和二個矩陣的列相乘時不會互相影響。假設A(m,n)表示矩陣的m行&#xff0c;n列。那么C(m,m)A(m,n) * B(n,m) &#xff1a;計算C矩陣時候分解成&#xff1a;process-1&#xf…

停止Java線程,小心interrupt()方法

轉自http://www.blogjava.NET/jinfeng_wang/archive/2008/04/27/196477.html ---------------------------------------------------------------------------------------------------- 程序是很簡易的。然而&#xff0c;在編程人員面前&#xff0c;多線程呈現出了一組新的難…

python輸入數學表達式并求值_用Python3實現表達式求值

一、題目描述 請用 python3編寫一個計算器的控制臺程序&#xff0c;支持加減乘除、乘方、括號、小數點&#xff0c;運算符優先級為括號>乘方>乘除>加減&#xff0c;同級別運算按照從左向右的順序計算。 二、輸入描述 數字包括"0123456789"&#xff0c;小數點…

mac上的mysql管理工具sequel pro

https://blog.csdn.net/wan_zaiyunduan/article/details/54909389 以前用過Plsql、Navicat、Workbench&#xff0c;現在換到mac上&#xff0c;用了現在這一款管理工具&#xff0c;很好用&#xff0c;所以推薦給大家。 完整的MySQL支持 Sequel Pro是一個快速,易于使用的Mac數據庫…