Octave/Matlab Tutorial
Octave/Matlab Tutorial
Basic Operations
你現在已經掌握不少機器學習知識了 在這段視頻中 我將教你一種編程語言 Octave語言 你能夠用它來非常迅速地 實現這門課中我們已經學過 或者將要學的 機器學習算法
過去我一直嘗試用不同的編程語言 來教授機器學習 包括C++、Java、 Python、Numpy 和 Octave 我發現當使用像 Octave這樣的 高級語言時 學生能夠更快 更好地學習 並掌握這些算法
事實上 在硅谷 我經常看到的情況是 進行大規模的 機器學習項目的人 通常會使用的 程序語言 就是Octave, Octave是一種很好的原始語言(prototyping language) 使用Octave 你能快速地實現你的算法 剩下的事情 你只需要 進行大規模的資源配置 你只用再花時間 用C++或Java這些語言 把算法重新實現就行了 因為我們知道 開發項目的時間 或者說你的時間 是很寶貴的 機器學習的時間也是很寶貴的 所以 如果你能 讓你的學習算法在Octave上快速的實現 基本的想法實現以后 再用C++或者Java去改寫 這樣 你就能節省出 大量的時間
據我所見 人們使用最多的 用於機器學習的原始語言 是Octave、MATLAB Python、NumPy 和 R Octave很好 因為它是開源的 當然 MATLAB也很好 但它不是每個人都 買得起的 但是 如果你能夠使用MATLAB 你也可以在這門課里面使用 如果你會Python、NumPy 或者R語言 我也見過有人用 R 的 但是 據我所知 這些人不得不中途放棄了 因為這些語言在開發上比較慢 而且 因為這些語言 Python、NumPy的語法 相較於Octave來說 還是更麻煩一點 正因為這樣 也因為我們最開始 用Octave來寫程序 所以我強烈建議你 不要用NumPy或者R來完整這門課的作業 我建議你 在這門課中 用Octave來寫程序 接下來 本視頻將快速地介紹 一系列的命令 目標是迅速地展示 通過這一系列Octave的命令 讓你知道Octave能用來做什么 我們的網站會提供 所有我在視頻中提到的 內容的文本 所以 當你看完這個視頻 想查詢一些命令時 你可以查看這些資料 這些都放在網上了
總之 我建議你 先看教學視頻 之后 把Octave安裝到電腦上 最后 去這門課的網站上 下載這門課的 相關文檔和視頻 然后 你可以試着 在Octave中鍵入一些 有趣的命令 讓程序運行在你的電腦上 這樣你可以看到程序是怎么運行的
讓我們開始吧 這里是我的Windows桌面 啟動Octave 現在打開Octave 這是Octave命令行 現在讓我示范 最基本的Octave代碼
輸入5 + 6 然后得到11 ;輸入3 - 2; 5×8;1/2;2 ^ 6 得到64; 這些都是基本的數學運算
>> 5+6 ans = 11 >> 3-2 ans = 1 >> 5*8 ans = 40 >> 1/2 ans = 0.50000 >> 2^6 ans = 64
你也可以做邏輯運算 例如 1==2 計算結果為 false ( 假 ) 這里的百分號命令表示注釋, 1==2 計算結果為假 這里用0表示;
1 ~= 2 這是真的 因此返回1 請注意 不等於符號的寫法 是這個波浪線加上等於符號 ( ~= ) 而不是等於感嘆號加等號 ( != ) 這是和其他一些 編程語言中不太一樣的地方
讓我們看看邏輯運算 1 && 0 使用雙&符號 表示邏輯與 1 && 0判斷為假 1和0的或運算 1 || 0 其計算結果為真
還有異或運算 如XOR ( 1, 0 ) 其返回值為1
>> 1 == 2 %false ans = 0 >> 1 ~= 2 %true ans = 1 >> 8>1 && 0 %AND ans = 0 >> 9>1 || 1 %OR ans = 1 >> xor(1,0) ans = 1
從左向右寫着 Octave 324.x版本 其計算結果等於11 這是默認的Octave提示 它顯示了當前Octave的版本 以及相關的其它信息 如果你不想看到那個提示 這里有一個隱藏的命令 輸入命令 PS('>> '); 現在你看到的就是等待命令的快捷提示 這句話在中間有一個字符串 ('>> '); 這是我喜歡的命令行樣子 這里敲一個回車 抱歉 寫錯了 這樣才對 要寫成PS1這樣 現在命令提示已經變得簡化了 這樣看起來很棒
>> PS1('>>>'); >>>
接下來 我們將談到Octave的變量 現在寫一個變量 對變量A賦值為3 並按下回車鍵 顯示變量A等於3 如果你想分配一個變量 但不希望在屏幕上顯示結果 你可以在命令后加一個分號 可以抑制打印輸出 敲入回車后 不打印任何東西。 A等於3 只是不顯示出來 其中這句命令不打印任何東西 現在舉一個字符串的例子 變量b等於"hi" 現在 如果我輸入b 則會顯示字符串變量b的值"hi" C等於3大於等於1 所以 現在C變量的值是真 如果你想打印出變量 或顯示一個變量 你可以像下面這么做 設置A等於圓周率π 如果我要打印該值 那么只需鍵入A 像這樣 就打印出來了
>>>a = 3 a = 3 >>>a = 3; #分號抑制打印 >>>
對於更復雜的屏幕輸出 也可以用DISP命令顯示 Disp( A )就相當於像這樣打印出A 你也可以用該命令來顯示字符串 輸入disp sprintf 小數 0.2% 逗號 A 像這樣 通過這條命令將打印出字符串 打印顯示為“兩位小數:3.14” 這是一種 舊風格的C語言語法 如果 就學過C語言的同學來說 你可以使用這種基本的語法來將結果打印到屏幕 Sprintf命令生成一個字符串 不僅僅是 字符串“2 decimal:3.14” 其中的“0.2%F”表示 代替A放在這里 並顯示A值的小數點后兩位數字 同時DISP 命令對字符串做出操作 DISP命令輸出 Sprintf產生的字符串 Sprintf命令 和DISP命令顯示字符串 再說一個細節 例如 sprintf命令的六個小數 0.6%F ,A 這應該打印π 的6位小數形式 最后 看起來像這樣 也有一些控制輸出長短格式的快捷命令 默認情況下 是字符串 顯示出的小數位有點多 短 ( short ) 格式 是默認的輸出格式 只是打印小數數位的第一位 相關這方面的內容還需要你繼續練習
>>>a = 3.14; >>>a a = 3.1400 >>>disp(a); 3.1400 >>>disp(sprintf('2 decimals: %0.2f', a)); 2 decimals: 3.14
>>>a=pi a = 3.1416 >>>format long >>>a a = 3.14159265358979 >>>format short >>>a a = 3.1416
下面 讓我們來看看向量和矩陣 比方說 建立一個矩陣A 輸入1 2 ; 3 4 ; 5 6 這會產生一個 三行兩列的矩陣A 其第一行是1 2 第二行是3 4 第三行是5 6 分號的作用 從本質上來說 就是在矩陣內換行到下一行 此外 還有其他的方法來建立矩陣A 輸入A矩陣的值 1 2 分號 3 4 分號 5 6 這是另一種方法 對A矩陣進行賦值 考慮到這是一個三行兩列的矩陣
>>>A = [1 2; 3 4; 5 6] A = 1 2 3 4 5 6 >>>a = [1 2; 3 4; 5 6] a = 1 2 3 4 5 6
你同樣可以用向量 建立向量V並賦值1 2 3 V是一個行向量 或者說是一個3 ( 列 )×1 ( 行 ) 的向量 一個胖胖的Y向量 或者說 一行三列的矩陣 注意不是三行一列 如果我想 分配一個列向量 我可以寫“1;2;3” 現在便有了一個 3 行 1 列 的向量 同時這是一個列向量 下面是一些更為有用的符號 V等於1:0.1:2 這個該如何理解呢 這個集合V是一組值 從數值1開始 增量或說是步長為0.1 直到增加到2 按照這樣的方法對向量V操作 可以得到一個行向量 這是一個1行11列的矩陣 其矩陣的元素是1 1.1 1.2 1.3 依此類推 直到數值2
>>>v = [1 2 3] v = 1 2 3 >>>v = [1; 2; 3] v = 1 2 3
>>>v = 1:0.1:2 v = Columns 1 through 4: 1.0000 1.1000 1.2000 1.3000 Columns 5 through 8: 1.4000 1.5000 1.6000 1.7000 Columns 9 through 11: 1.8000 1.9000 2.0000
>>>v = 1:6 v = 1 2 3 4 5 6
現在 我也可以 建立一個集合V並用命令“1:6”進行賦值 這樣V就被賦值了 1至6的六個整數 這里還有一些其他的方法來生成矩陣 例如“ones(2, 3)” 也可以用來生成矩陣 其結果為一個兩行三列的矩陣 不過矩陣中的所有元素都為1 當我想生成一個 元素都為2 兩行三列的矩陣 就可以使用這個命令 你可以把這個方法當成一個 生成矩陣的快速方法 當你想生成一個三維2×2×2的矩陣時 你就可以用這個“ones”命令 比方說 w是一個有三個1的 行向量 或者說一行 由三個同樣的1組成的向量 你也可以說 w為一個 一行三列的零矩陣 一行三列的A矩陣里的元素全部是零
>>>ones(2,3) ans = 1 1 1 1 1 1 >>>w = ones(1,3) w = 1 1 1
還有很多的方式來生成矩陣 如果我對W進行賦值 用Rand命令建立一個一行三列的矩陣 因為使用了Rand命令 則其一行三列的元素均為隨機值 如果我使用 “rand(3, 3)”命令 這就生成了一個 3×3的矩陣 並且其所有元素均為隨機 數值介於0和1之間 所以 正是因為這一點 我們可以得到 數值均勻介於0和1之間的元素
>>>w = rand(3,3) w = 0.91025 0.82671 0.14067 0.90400 0.34350 0.51289 0.25501 0.24975 0.80750
如果 你知道什么是高斯隨機變量 或者 你知道什么是正態分布的隨機變量 你可以設置集合W 使其等於一個一行三列的N矩陣 並且 來自三個值 一個平均值為0的高斯分布 方差 或者等於1的標准偏差 還可以設置地更復雜
벵>>>w = randn(1,3) w = -0.052546 -1.786869 0.754202
例如 W減去6 再加上10的平方 兩者相乘 Rand命令生成一個1行10000列的矩陣 把分號放到末尾 這樣結果就打印不出來 那這樣會得到什么呢 這樣就可以 得到 一個有10000元素的向量 想知道具體是多少 我們也可把它打印出來 這將產生一個這樣的矩陣 生成了這個叫做 data 的對象 是吧? 這就是一個 有着10000個元素的矩陣W 如果我現在 用繪制直方圖命令 繪制出一個直方圖 使用Octave的 打印直方圖命令 你只需要數秒鍾就可以將它繪制出來 這是一個對隨機變量W 繪制出的直方圖 這里是-6+0 乘上十倍的高斯隨機變量 這樣 可以繪制出一個 有着更多條的 乃至50個條的直方圖來 這樣 就有一個 均值減去6的高斯直方圖 因為這里是 -6加10的平方根並與這項相乘 因此 這個高斯隨機變量的方差 是10 且其標准偏差為10的平方根 3.1
w = -6 + sqrt(10)*(randn(1,10000)); hist(w) hist(w, 50)
最后 說一個生成矩陣的 特殊命令I 其實 I也可說是一個雙關語字標識 設置一個4階單位矩陣 這是一個4×4矩陣 所以I為“eye(4)” 通過上面的命令得到4×4矩陣 I可以等於5階單位陣 6階單位陣 那么就有 6階單位陣 eye( 3 )是一個3階方陣
>> eye(4) ans = Diagonal Matrix 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0 1
在本節視頻的最后 還有一個比較有用的命令 那就是幫助命令 例如 你可以鍵入help i 它就會將矩陣的相關信息顯示出來 命令Q可以退出Octave 你也可以鍵入help rand 將會顯示出有關rand函數的相關幫助文檔 以及相關的隨機數生成函數 甚至可以使用命令help help 將會顯示出help命令的使用方法
>> help For help with individual commands and functions type help NAME (replace NAME with the name of the command or function you would like to learn more about). For a more detailed introduction to GNU Octave, please consult the manual. To read the manual from the prompt type doc GNU Octave is supported and developed by its user community. For more information visit http://www.octave.org.
以上講解的內容 都是Octave的基本操作 希望你能通過上面的講解 自己練習一些矩陣、乘、加等操作 將這些操作在Octave中熟練 在接下來的視頻中 將會涉及 更多復雜的命令 並使用它們在Octave中對數據進行更多的操作
Moving Data Around移動數據
在第二段關於 Octave的 輔導課視頻中 我將開始介紹 如何在 Octave 中移動數據 具體來說 如果你有一個機器學習問題 你怎樣把數據加載到 Octave 中? 怎樣把數據存入一個矩陣? 如何對矩陣進行相乘? 如何保存計算結果? 如何移動這些數據 並用數據進行操作?
和之前一樣 這是我的 Octave 窗口 我們繼續沿用上次的窗口 我鍵入 A 得到我們之前構建的矩陣 A 也就是用這個命令生成的 A = 這是一個三行二列的矩陣
Octave 中的 size() 命令 返回矩陣的尺寸 所以 size(A) 命令返回3 2 實際上 size() 命令返回的 是一個 1×2 的矩陣 我們可以用 sz 來存放 設置 sz = size(A) 因此 sz 就是一個1×2的矩陣 第一個元素是3 第二個元素是2 所以如果鍵入 size(sz) 看看 sz 的尺寸 返回的是1 2 表示是一個1×2的矩陣 1 和 2 分別表示 矩陣 A 的維度 (此處口誤 應為 sz 的維度 譯者注)
>> A = [1 2; 3 4; 5 6] A = 1 2 3 4 5 6 >> size(A) ans = 3 2 >> sz = size(A) sz = 3 2 >> size(sz) ans = 1 2
>> size(A,1) ans = 3 >> size(A,2) ans = 2
你也可以鍵入 size(A, 1) 這個命令會返回 A 矩陣的第一個元素 A 矩陣的第一個維度的尺寸 也就是 A 矩陣的行數 同樣 命令 size(A, 2) 將返回2 也就是 A 矩陣的列數 也就是 A 矩陣的列數 如果你有一個向量 v 假如 v = 假如 v = 然后鍵入 length(v) 這個命令將返回 最大維度的大小 你也可以鍵入 length(A) 由於矩陣 A 是一個3×2的矩陣 因此最大的維度 應該是3 因此該命令會返回3 但通常我們還是對向量使用 length 命令 比如 length() 比如 length() 而不是對矩陣使用 length 命令 因為畢竟有點容易讓人弄混
>> V = [1 2 3 4] V = 1 2 3 4 >> length(V) ans = 4 >> length(A) ans = 3
下面讓我們來看看 如何在系統中 加載數據和尋找數據 當我們打開 Octave 時 我們通常已經在一個 默認路徑中 這個路徑是 Octave 的安裝位置 pwd 命令可以顯示出 Octave 當前所處路徑 Octave 當前所處路徑 所以現在我們就在這個目錄下 cd 命令 意思是改變路徑 我可以把路徑改為C:\Users\ang\Desktop 這樣當前目錄就變為了桌面 如果鍵入 ls ls 來自於一個 Unix 或者 Linux 命令 ls 命令將列出 我桌面上的所有路徑 因此這些就是 我桌面上的所有文件了
>> pwd ans = C:\Users\xin >> cd 'E:\TEMPsrc\octave' >> pwd ans = E:\TEMPsrc\octave >> ls
事實上 我的桌面上 有兩個文件 featuresX.dat 和 priceY.dat 是兩個我想解決的機器學習問題 這是我的桌面 這是 featuresX 文件 featuresX 文件如這個窗口所示 是一個含有兩列數據的文件 這其實就是我的房屋價格數據 我想應該是 數據集中有47行 第一個房子樣本 面積是2104平方英尺 有3個卧室 第二套房子面積為1600 有3個卧室 等等 priceY 是這個文件 也就是 訓練集中的價格數據 所以 featuresX 和 priceY 就是兩個存放數據的文檔 那么應該怎樣把數據讀入 Octave 呢? 好的 我們只需要鍵 鍵入 featuresX.dat 這樣 我將加載了 featuresX 文件 同樣地我可以加載 priceY.dat 其實有好多種辦法可以完成 如果你把命令寫成 字符串的形式 load('featureX.dat') 也是可以的 這里打錯了 這跟剛才的命令效果是相同的 只不過是把文件名 寫成了一個字符串的形式 現在文件名被存在一個 字符串中 Octave 中使用引號 來表示字符串 就像這樣 這就是一個字符串 因此我們讀取的文件 文件名由這個字符串給出
load featuresX.dat load priceY.dat load('featuresX.dat')
另外 who 命令 能顯示出 在我的 Octave 工作空間中的所有變量 因此 who 命令顯示出 當前 Octave 儲存的變量 包括 featureX 和 priceY 同樣還包括 在此之前你創建的 那些變量
>> who Variables in the current scope: a ans b c
所以我可以鍵入 featuresX 回車 來顯示 featuresX 這些就是存在里面的數據 還可以鍵入 size(featuresX) 得出的結果是 47 2 代表這是一個47×2的矩陣 類似地 輸入 size(priceY) 結果是 47 1 表示這是一個47維的向量 是一個列矩陣 存放的是訓練集中的所有價格 Y 的值 who 函數能讓你看到 當前工作空間中的所有變量
同樣還有另一個 whos 命令 能更詳細地進行查看 因此 在 who 后面加一個 s 同樣也列出我所有的變量 不僅如此 還列出了變量的維度 我們看到 A 是一個 3×2的矩陣 X 是一個47×2的矩陣 priceY 是一個47×1的矩陣 也就是一個向量 同時還顯示出 需要占用多少內存空間 以及數據類型是什么 double 意思是雙精度浮點型 這也就是說 這些數都是實數 是浮點數
>> whos Variables in the current scope: Attr Name Size Bytes Class ==== ==== ==== ===== ===== a 1x1 8 doubl e ans 1x17 17 char b 1x1 8 doubl e c 1x1 8 doubl e d 3x2 48 doubl e Total is 26 elements using 89 bytes
如果你想刪除某個變量 你可以使用 clear 命令 因此 我們鍵入 clear featuresX 然后再輸入 whos 命令 你會發現 featuresX 消失了
>> who Variables in the current scope: a ans b c d >> clear a >> who Variables in the current scope: ans b c d
另外 我們怎么儲存數據呢? 我們來看 我們設變量 v 為 priceY(1:10) 這表示的是將向量 Y 的 前10個元素存入 v 中 我們輸入 who 或者 whos Y 是一個47×1的向量 因此現在 v 就是10×1的向量 因為剛才設置了 v = priceY(1:10) 這便將 v 的值 設為了 Y 的前十個元素 假如我們想把它存入硬盤 那么用 save hello.mat v 命令 這個命令 會將變量 v 存成一個叫 hello.mat 的文件 讓我們回車 現在我的桌面上 就出現了一個新文件 名為 hello.mat 由於我的電腦里 也同時安裝了 MATLAB 所以這個圖標 上面有 MATLAB 的標識 因為操作系統把文件識別為 MATLAB 文件 所以如果在你的電腦上 圖標顯示的不一樣的話 也沒有關系
>> save hello.mat d
現在我們清除所有變量 直接鍵入 clear 這樣將刪除工作空間中的所有變量 所以現在工作空間中啥都沒了
>> clear
>> who
但如果我載入 hello.mat 文件 我又重新讀取了變量 v 因為我之前 把變量 v存入了 hello.mat 文件中 所以我們剛才用 save 命令做了什么 這個命令把數據 按照二進制形式儲存 或者說是更壓縮的二進制形式 因此 如果 v 是很大的數據 那么壓縮幅度也更大 占用空間也更小 如果你想把數據 存成一個人能看懂的形式 那么可以鍵入 save hello.txt v -ascii 這樣就會把數據 存成一個文本文檔 或者將數據的 ascii 碼存成文本文檔 現在 我鍵入了這個命令以后 我的桌面上 就有了 hello.txt 文件 就有了 hello.txt 文件 如果打開它 我們可以發現 這個文本文檔存放着我們的數據 這就是讀取和儲存數據的方法
>> v = [1 2; 3 4; 5 6; 7 8; 9 0] v = 1 2 3 4 5 6 7 8 9 0 < -ascii %save as text(ASCII)
接下來我們再來講講操作數據的方法 假如 A 還是那個矩陣 跟剛才一樣還是那個 3×2 的矩陣 現在我們加上索引值 比如鍵入 A(3,2) 這將索引到 A 矩陣的 (3,2) 元素 A 矩陣的 (3,2) 元素 這就是我們通常 書寫矩陣的形式 寫成 A 下標32 下標32
3和2分別表示 矩陣的第三行 和第二列對應的元素 因此也就對應 6 我也可以鍵入 A(2,:) 來返回 第二列的所有元素 因此 冒號表示 該行或該列的所有元素 因此 A(2,:) 表示 A 矩陣的第二行的所有元素 類似地 如果我鍵入 A(:,2) 這將返回 A 矩陣第二列的所有元素 這將得到 2 4 6 這表示返回 A 矩陣的第二列的所有元素 因此這就是 矩陣 A 的第二列 就是 2 4 6 你也可以在運算中 使用這些較為復雜的索引
>> A = [1 2; 3 4; 5 6] A = 1 2 3 4 5 6 >> A(3,2) ans = 6 >> A(2,:) ans = 3 4 >> A(:,2) ans = 2 4 6
我再給你展示幾個例子 可能你也不會經常使用 但我還是輸入給你看 A(,:) 這個命令意思是 取 A 矩陣第一個索引值為1或3的元素 也就是說我取的是 A 矩陣的第一行和 第三行的每一列 第三行的每一列 這是 A 矩陣 因此 輸入 A(, :) 返回第一行 返回第三行 冒號表示的是 取這兩行的每一列元素 也就是第一行 和第二行的所有元素(此處口誤 應為第三行 譯者注) 因此返回結果為 1 2 5 6
>> A([1 3], :) ans = 1 2 5 6
可能這些比較復雜一點的 索引操作 你不會經常用到 我們還能做什么呢 這依然是 A 矩陣 A(:,2) 命令返回第二列 你也可以為它賦值 所以我可以取 A 矩陣的第二列 然后將它賦值為 10 11 12 如果我這樣做的話 我實際上是取出了 A 的第二列 然后把一個列向量賦給了它 因此現在 A 矩陣的第一列還是 1 3 5 第二列就被替換為 10 11 12 接下來一個操作 讓我們把 A 設為 A = ] 這樣做的結果是 在原矩陣的右邊 附加了一個新的列矩陣 附加了一個新的列矩陣 現在 見證奇跡的時刻... 噢 我又犯錯了 應該放分號的 現在 A 矩陣就是這樣了 明白嗎? 我希望你聽懂了 所以 這是個列矩陣 而我們所做的 就是把 A 矩陣設置為 原來的 A 矩陣 再在右邊附上一個 新添加的列矩陣 我們的原矩陣 A 就是右邊這個6個元素 就是右邊這個6個元素 所以我們就是把 A 矩陣 右邊加上了一個 新的列向量 所以現在 A 矩陣 變成這樣一個 3×3 的矩陣
>> A(:,2) = [10;11;12] A = 1 10 3 11 5 12 >> A = [A, [100;101;102]] A = 1 10 100 3 11 101 5 12 102
最后 還有一個小技巧 我也經常使用 如果你就輸入 A(:) 這是一個很特別的語法結構 意思是把 A 中的所有元素 放入一個單獨的列向量 這樣我們就得到了一個 9×1 的向量 這些元素都是 A 中的元素排列起來的
>> A(:) ans = 1 3 5 10 11 12 100 101 102
再來幾個例子好了 我還是把 A 重新設為 假如說 我再設一個 B 為 我可以新建一個矩陣 C C = 這個意思就是 這是我的矩陣 A 這是我的矩陣 B 我設 C = 這樣做的結果就是 把這兩個矩陣直接連在一起 矩陣 A 在左邊 矩陣 B 在右邊 這樣組成了 C 矩陣 就是直接把 A 和 B 合起來 我還可以設 C = 這里的分號表示 把分號后面的東西放到下面 所以 的作用 依然還是把兩個矩陣 放在一起 只不過現在是上下排列 所以現在 A 在上面 B 在下面 C 就是一個 6×2 矩陣 簡單地說 分號的意思就是換到下一行 所以 C 就包括上面的 A 然后換行到下面 然后在下面放上一個 B 另外順便說一下 這個命令 跟 是一樣的 這兩種寫法的結果是相同的
>> A = [1 2; 3 4; 5 6]; >> B = [11 12; 13 14; 15 16]; >> C = [A B] C = 1 2 11 12 3 4 13 14 5 6 15 16
>> C = [A; B] C = 1 2 3 4 5 6 11 12 13 14 15 16
好了 通過以上這些操作 希望你現在掌握了 怎樣構建矩陣 也希望我展示的這些命令 能讓你很快地學會 怎樣把矩陣放到一起 怎樣取出矩陣 並且把它們放到一起 組成更大的矩陣 通過幾句簡單的代碼 Octave 能夠很方便地 很快速地幫助我們 組合復雜的矩陣以及對數據進行移動 這就是移動數據這一節課
在下一段視頻中 我們將一起來談談 怎樣利用數據進行更為復雜的計算 希望這節課的內容 能讓你明白 在 Octave 中 怎樣用幾句簡單的命令 很快地對數據進行移動 包括加載和儲存一個向量 或矩陣 加載和存儲數據 把矩陣放在一起 構建更大的矩陣 用索引對矩陣某個特定元素進行操作等等 我知道可能我一下子 講了很多命令 所以我認為對你來講
最好的學習方法是 下課后復習一下我鍵入的這些代碼 好好地看一看 從課程的網上 把代碼的副本下載下來 重新好好看看這些副本 然后自己在 Octave 中 把這些命令重新輸一遍 慢慢開始學會使用這些命令 當然 沒有必要把這些命令都記住 你也不可能記得住 你要做的就是 從這段視頻里 了解一下你可以用哪些命令 做哪些事 這樣在你今后需要 編寫學習算法時 如果你要找到某個 Octave 中的命令 你可能回想起 你之前在這里學到過 然后你就可以查找 課程中提供的程序副本 這樣就能很輕松地找到 你想使用的命令了 好了 這就是 移動數據這節課的全部內容
在下一段視頻中 我將開始向你介紹 怎樣進行一些 更復雜的計算 怎樣對數據進行計算 怎樣對數據進行計算 同時開始實現學習算法
Computing on Data
現在 你已經學會了在Octave中 如何加載或存儲數據 如何把數據存入矩陣 等等 在這段視頻中 我將向你介紹 如何對數據進行運算 稍后我們將使用這些 運算操作來實現我們的學習算法
現在我們開始吧 這是我的 Octave 窗口 我現在快速地 初始化一些變量 比如設置A 為一個3×2的矩陣 設置B為 一個3 × 2矩陣 設置C為 2 × 2矩陣 現在 我想算兩個矩陣的乘積 比如說 A × C 我只需鍵入A×C 這是一個 3×2 矩陣乘以 2×2 矩陣 得到這樣一個3×2矩陣 你也可以對每一個元素 做運算 方法是做點乘運算A .*B 這么做 Octave將矩陣 A 中的每一個元素 與矩陣 B 中的 對應元素相乘 這是A 這是B 這是A .* B 比如說 這里第一個元素 1乘以11得到11 第二個元素是 2乘以12得到24 這就是兩個矩陣的 元素位運算 通常來說 在Octave中 點號一般 用來表示元素位運算 這里是一個矩陣A 這里我輸入A .^ 2 這將對矩陣A中 每一個元素平方 所以 1的平方是1 2的平方是4 等等
>> A = [1 2; 3 4; 5 6]; >> B = [11 12; 13 14; 15 16]; >> C = [1 1; 2 2]; >> A*C ans = 5 5 11 11 17 17 >> A .* B ans = 11 24 39 56 75 96 >> A .^ 2 ans = 1 4 9 16 25 36
我們設V是一個向量 設V為 是列向量 你也可以輸入 1 ./ V 得到每一個元素的倒數 所以這樣一來 就會分別算出 1/1 1/2 1/3 矩陣也可以這樣操作 1 ./ A 得到 A中每一個元素的倒數
>> V = [1; 2; 3]; >> 1 ./ V ans = 1.00000 0.50000 0.33333 >> 1 ./ A ans = 1.00000 0.50000 0.33333 0.25000 0.20000 0.16667
同樣地 這里的點號 還是表示對每一個元素進行操作 我們還可以進行求對數運算 也就是對每個元素 進行求對數運算 還有自然數e的冪次運算 就是以e為底 以這些元素為冪的運算 所以這是e 這是e的平方 這是e的立方 v 矩陣是這樣的 我還可以用 abs 來對 v 的每一個元素 求絕對值 當然這里 v 都是正數 我們換成另一個 這樣對每個元素求絕對值 得到的結果就是 這些非負的元素 還有 -v 給出V中每個元素的相反數 這等價於 -1 乘以 v 不過一般就直接用 -v 就好了 其實就等於 -1*v 還有什么呢?
>> log(V) ans = 0.00000 0.69315 1.09861 >> exp(V) ans = 2.7183 7.3891 20.0855 >> abs(V) ans = 1 2 3
還有一個技巧 比如說 我們想對v中的每個元素都加1 那么我們可以這么做 首先構造一個 3行1列的1向量 然后把這個1向量跟原來的向量相加 因此 v 向量從 增至 我用了一個 length(v) 命令 因此這樣一來 ones(length(v) ,1) 就相當於 ones(3,1) 所以這是ones(3,1) 對吧 然后我做的是 v + ones(3,1) 也就是將 v 的各元素 都加上這些1 這樣就將 v 的每個元素 增加了1 另一種更簡單的方法是 直接用 v+1 所以這是 v v + 1 也就等於 把 v 中的每一個元素 都加上1
>> v = [1;2;3] v = 1 2 3 >> v + ones(length(v), 1) ans = 2 3 4 >> v + ones(3,1) ans = 2 3 4 >> v + 1 ans = 2 3 4
現在 讓我們來談談更多的操作 這是我的矩陣A 如果你想要求它的轉置 那么方法是用A‘ 這是單引號符號 並且是左引號 可能你的鍵盤上 有一個左引號 和一個右引號 這里用的是左引號 也就是標准的引號 因此 A’ 將得出 A 的轉置矩陣 當然 如果我寫 (A‘)’ 也就是 A 轉置兩次 那么我又重新得到矩陣 A
>> A A = 1 2 3 4 5 6 >> A' ans = 1 3 5 2 4 6
還有一些有用的函數 假如說 小寫a 是 這是一個1行4列矩陣 假如說 val=max(a) 這將返回 A矩陣中的最大值 在這里是15 我還可以寫 = max(a) 這將返回 a矩陣中的最大值 存入val 以及該值對應的索引 因此元素15對應的索引值為2 存入ind 所以 ind 等於2
>> a = [1 15 2 0.5] a = 1.00000 15.00000 2.00000 0.50000 >> val = max(a) val = 15 >> [val, ind] = max(a) val = 15 ind = 2
特別注意一下 如果你用命令 max(A) A是一個矩陣的話 這樣做就是對每一列 求最大值 等下再仔細講講
我們還是用這個例子 這個 小a 矩陣 如果輸入 a<3 這將進行逐元素的運算 所以 第一個元素 是小於3的 因此返回1 a的第二個元素 不小於3 所以 這個值是0 表示"非" 第三個和第四個數字 仍然是小於3 2和0.5都小於3 因此 這返回 也就是說 對a矩陣的每一個元素 與3進行比較 然后根據每一個元素與3的大小關系 返回1和0表示真與假
現在 如果我寫 find(a<3) 這將告訴我 a 中的哪些元素 是小於3的 是小於3的 在這里就是第一 第三和第四個元素 是小於3的
a = 1.00000 15.00000 2.00000 0.50000 >> a < 3 ans = 1 0 1 1 >> find(a < 3) ans = 1 3 4
下一個例子 設A = magic(3) magic 函數返回什么呢 讓我們查看 magic 函數的幫助文件
magic 函數將返回 一個矩陣 稱為魔方陣或幻方 (magic squares) 它們具有以下 這樣的數學性質 它們所有的行和列和對角線 加起來都等於相同的值 當然據我所知 這在機器學習里 基本用不上 但我可以用這個方法 很方便地生成一個 3行3列的矩陣 而這個魔方矩陣這神奇的方形屏幕。 每一行 每一列 每一個對角線 三個數字加起來 都是等於同一個數 我只有在演示功能 或者上課教 Octave 的時候 會用到這個矩陣 在其他有用的機器學習應用中 這個矩陣其實沒多大作用 讓我來看看別的 如果我輸入 = find( A>=7 ) 這將找出所有A矩陣中 大於等於7的元素 因此 r 和 c 分別表示行和列 這就表示 第一行第一列的元素大於等於7 第三行第二列的元素大於等於7 第二行第三列的元素大於等於7 我們來看看 第二行第三列的元素 就是 A(2,3) 是等於7的 就是這個元素 確實是大於等於7的 順便說一句 其實我從來都 不去刻意記住這個 find 函數 到底是怎么用的 我只需要會用 help 函數就可以了 每當我在使用這個函數 忘記怎么用的時候 我就可以用 help 函數 鍵入 help find 來找到幫助文檔
>> A = magic(3) A = 8 1 6 3 5 7 4 9 2 >> [r, c] = find(A >= 7) r = 1 3 2 c = 1 2 3
好吧 最后再講兩個內容 一個是求和函數 這是 a 矩陣 鍵入 sum(a) 就把 a 中所有元素加起來了 如果我想把它們都乘起來 鍵入 prod(a) prod 意思是 product(乘積) 它將返回 這四個元素的乘積 floor(a) 是向下四舍五入 因此對於 a 中的元素 0.5將被下舍入變成0 還有 ceil(A) 表示向上四舍五入 所以0.5將上舍入變為 最接近的整數 也就是1
>> a a = 1.00000 15.00000 2.00000 0.50000 >> sum(a) ans = 18.500 >> prod(a) ans = 15 >> floor(a) ans = 1 15 2 0 >> ceil(a) ans = 1 15 2 1
還有 我們來看 鍵入 type(3) 這通常得到一個3×3的矩陣 如果鍵入 max(rand(3), rand(3)) 這樣做的結果是 返回兩個3×3的隨機矩陣 並且逐元素比較 取最大值 所以 你會發現所有這些 數字幾乎都比較大 因為這里的每個元素 都實際上是 兩個隨機生成的矩陣 逐元素進行比較 取最大的那個值 這是剛才生成的 3×3魔方陣 A 假如我輸入 max(A,[],1) 這樣做會得到 每一列的最大值 所以第一例的最大值 就是8 第二列是9 第三列的最大值是7 這里的1表示 取A矩陣第一個維度的最大值 相對地 如果我鍵入 max(A,[],2) 這將得到每一行的最大值 所以 第一行的最大值 是等於8 第二行最大值是7 第三行是9 所以你可以用這個方法 來求得每一行或每一列的最值
>> max(rand(3), rand(3)) ans = 0.957477 0.083887 0.459507 0.799441 0.975439 0.927632 0.888604 0.942436 0.612661 >> A A = 8 1 6 3 5 7 4 9 2 >> max(A, [], 1) ans = 8 9 7
另外 你要知道 默認情況下 max(A)返回的是 每一列的最大值 如果你想要 找出整個矩陣A的最大值 你可以輸入 max(max(A)) 像這樣 或者你可以將 A 矩陣轉成 一個向量 然后鍵入 max(A(:)) 這樣做就是把 A 當做一個向量 並返回 A 向量中的最大值
>> max(max(A)) ans = 9 >> max(A(:)) ans = 9
最后 讓我們把 A 設為一個 9行9列的魔方陣 別忘了 魔方陣具有的特性是 每行每列和對角線的求和都是相等的 這是一個9×9的魔方陣 我們來求一個 sum(A,1) 這樣就得到每一列的總和 所以這樣做就是 把 A 的每一列進行求和 從這里我們也可以看出 這也驗證了 一個9×9的魔方陣 確實每一列加起來都相等 都為369 現在我們來求每一行的和 鍵入sum(A,2) 這樣就得到了 A 中每一行的和 A 中每一行的和 加起來還是369
>> A = magic(5) A = 17 24 1 8 15 23 5 7 14 16 4 6 13 20 22 10 12 19 21 3 11 18 25 2 9 >> sum(A,1) ans = 65 65 65 65 65 >> sum(A,2) ans = 65 65 65 65 65
現在我們來算 A 的對角線元素的和 看看它們的和 是不是也相等 我們現在構造一個 9×9 的單位矩陣 鍵入 eye(9) 設為I9 然后我們要用 A 逐點乘以這個單位矩陣 這是矩陣A 我現在用 A 逐點乘以 eye(9) 這樣做的結果是 兩個矩陣對應元素 將進行相乘 除了對角線元素外 其他元素都會得到0 然后我對剛才求到的結果 鍵入sum(sum(A.*eye(9)) 這實際上是求得了 這個矩陣對角線元素的和 確實是369
>> sum(sum(A.*eye(5))) ans = 65
你也可以求另一條對角線的和 這個是從左上角到右下角的 你也可以求另一條對角線 從左下角到右上角 這個和 這個命令會有點麻煩 其實你不需要知道這個 我只是想給你看 如果你感興趣的話可以聽聽 讓我們來看看 flipup/flipud 表示向上/向下翻轉 如果你用這個命令的話 計算的就是副對角線上 所有元素的和 還是會得到369 我來給你演示一下 eye(9) 矩陣是這樣 那么 flipup(eye(9)) 將得到一個單位矩陣 並且將它翻轉 不好意思打錯了 應該是flipud 翻轉以后所有的1就變成副對角線了
>> eye(9) ans = Diagonal Matrix 1 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 1 >> flipud(eye(9)) ans = Permutation Matrix 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0
最后再說一個命令 然后就下課 假如 A 是一個 3×3的魔方陣 同樣地 如果你想 這個矩陣的逆矩陣 鍵入 pinv(A) 通常稱為偽逆矩陣 但這個名字不重要 你就把它看成是 矩陣 A 求逆 因此這就是 A 矩陣的逆矩陣 設 temp = pinv(A) 然后再用temp 乘以 A 這實際上得到的就是 單位矩陣 對角線為1 其他元素為0 稍微圓整一下就是 好了 這樣我們就介紹了 如何對矩陣中的數字 進行各種操作
>> A = magic(3) A = 8 1 6 3 5 7 4 9 2 >> pinv(A) ans = 0.147222 -0.144444 0.063889 -0.061111 0.022222 0.105556 -0.019444 0.188889 -0.102778 >> temp = pinv(A) temp = 0.147222 -0.144444 0.063889 -0.061111 0.022222 0.105556 -0.019444 0.188889 -0.102778 >> temp * A ans = 1.00000 0.00000 -0.00000 -0.00000 1.00000 0.00000 0.00000 0.00000 1.00000
在運行完某個 學習算法之后 通常一件最有用的事情 是看看你的結果 或者說讓你的結果可視化 在接下來的視頻中 我會非常迅速地告訴你 如何很快地畫出 如何只用一兩行代碼 你就可以快速地可視化你的數據 畫出你的數據 這樣你就能更好地理解 你使用的學習算法
Plotting Data
當開發學習算法時 往往幾個簡單的圖 可以讓你更好地 理解算法的內容 並且可以完整地檢查下 算法是否正常運行 是否達到了算法的目的 例如在之前的 視頻中 我談到了 繪制成本函數J(θ) 可以幫助 確認梯度下降算法是否收斂 通常情況下 繪制數據 或學習算法所有輸出 也會啟發你 如何改進你的學習算法 幸運的是 Octave有非常 簡單的工具用來生成大量 不同的圖 當我用學習算法時 我發現 繪制數據 繪制學習算法等 往往是 我獲得想法 來改進算法的重要部分 在這段視頻中 我想告訴你一些 Octave的工具來繪制和可視化你的數據
這是我的 Octave 窗口 我們先來快速生成一些數據 用來繪圖 我先設置t 等於這個數列 這是t 是從0到0.98的集合 讓我們設置y1等於sin 2*pi*4*t (此處pi表示π) 如果我想繪制正弦函數 這是很容易的 我只需要輸入plot(t, y1) 並回車 就出現了這個圖 橫軸是 t變量 縱軸是y1 也就是我們 剛剛所輸出的正弦函數
>> t=[0:0.01:0.98]; >> y1 = sin(2*pi*4*t); >> plot(t,y1);
讓我們設置y2 等於cos 2*pi*4*t 而如果我輸入plot t逗號y2 Octave將會 消除之前的正弦圖 並且用這個余弦圖來代替它 這里縱軸cos(x)從1開始
>> t=[0:0.01:0.98]; >> y2 = cos(2*pi*4*t); >> plot(t,y2);
如果我 要同時表示 正弦和余弦曲線 我要做的就是 輸入plot(t, y1) 這是我的正弦函數 我使用函數hold on hold on函數 的功能是將 新的圖像繪制在 舊的之上 我現在繪制t y2 我要以不同的顏色繪制余弦函數 所以我在這里輸入 帶引號的r 我將繪制余弦函數 在這之上 而不是替換了現有的圖 r表示所使用的顏色 再加上命令xlabel('time') 來標記X軸即水平軸 輸入ylabel('value') 來標記垂直軸的值 同時我也可以
來標記我的兩條函數曲線 用這個命令 legend('sin', 'cos') 將這個 圖例放在右上方 表示這兩條曲線表示的內容 最后輸入title('myplot') 在圖像的頂部顯示這幅圖的標題 如果你想保存 這幅圖像,你輸入print -dpng 'myplot.png' png是一個圖像 文件格式 如果你 這樣做了 它可以讓你保存為一個文件 如果我這樣做 讓我先改一下路徑 像這樣 然后我將它打出來 這需要一點時間 而這取決於你的 Octave的配置設置 可能需要幾秒鍾 但改變 路徑到我的桌面 現在Octave需要幾秒鍾的時間來保存它 如果我現在去到我的桌面 先最小化這些窗口 這就是 Octave所保存的myplot.png 這就是保存為PNG的文件
>> plot(t, y1); >> hold on; >> plot(t, y2, 'r'); >> xlabel('time') >> ylabel('value') >> legend('sin', 'cos') >> title('my plot') >> print -dpng 'myplot.png'
Octave也可以保存為很多其他的格式 你可以鍵入help plot 如果你想試試 其他格式的文件 而不是 PNG 你可以把圖片 保存為其他格式 最后如果你想 刪掉這個圖像 命令close會讓這個圖像關掉 如果我鍵入 close 這個圖像 就從我的桌面消失了
>> close
Octave也可以讓你為圖像標號 你鍵入figure(1); plot(t, y1); 將顯示 第一張圖 繪制了變量t y1 如果你想繪制第二個圖 你可以指定一個不同的數字編號 鍵入figure(2); plot(t, y2); 正如這樣 現在我的桌面上 其實有2個圖 圖1和圖2 此時一個繪制正弦 函數 另一個繪制了余弦函數
>> figure(1); plot(t, y1); >> figure(2); plot(t, y2);
這是另一個我經常使用的命令 subplot命令 我們要使用subplot(1,2,1) 它將圖像 分為一個 1*2的格子 也就是前兩個參數 然后它使用 第一個格子 也就是最后一個參數1的意思 所以,將我的圖像分成 1*2的格子 我現在使用 第一個格子 如果我鍵入這個 那么這個圖像顯示在左邊 如果鍵入plot(t, y1) 現在這個圖 顯示在第一個格子 如果我鍵入subplot(1,2,2) 那么我就要 使用第二個格子 鍵入plot(t, y2); 現在y2顯示在右邊 也就是第二個格子 最后一個命令 你可以 改變軸的刻度 比如改成 也就是設置了 右邊圖的x軸 和y軸的范圍 具體而言 它將 右圖中的橫軸 的范圍調整至0.5到1 豎軸的范圍為 -1到1 而且 你不需要記住所有這些命令 如果你需要 改變坐標軸或者 需要知道axis命令 你可以 用Octave中 help命令了解細節
>> subplot(1,2,1); >> plot(t, y1); >> subplot(1,2,2); >> plot(t, y2); >> axis([0.5 1 -1 1])
最后 還有幾個命令 clf清除 一幅圖像 這里有一個獨特的特點 讓我們設置A等於 一個5×5 magic方陣 現在A是這個5*5 的矩陣 我有時用一個巧妙的方法 來可視化矩陣 也就是imagesc(A) 它將會 繪制一個5*5的矩陣 一個5*5的彩色格圖 不同的顏色對應 A矩陣中的不同值 具體地說 我還可以使用函數colorbar 讓我用一個 更復雜的命令 imagesc(A) colorbar colormap gray 這實際上是在同一時間運行三個命令 運行imagesc然后運行 colorbar 然后運行colormap gray 它生成了 一個顏色圖像 一個灰度分布圖 並在 右邊也加入一個顏色條 所以這個顏色條 顯示不同深淺的顏色所對應的值
>> clf; >> A = magic(5); >> imagesc(A) >> imagesc(A), colorbar, colormap gray;
具體地 左上 A矩陣的元素 是17 所以對應 的是這樣中等的灰度 而與此相反的第二個 元素 也就是 A(1,2)元素 代表的值為24 它對應於 這里的這個方塊 是接近白色的灰度 較小的值比如 A多少呢 A(4,5) 為3對應着 你可以看到在我的顏色條 它對應於 一個更暗的灰度 這里是另一個例子 我可以繪制一個較大的 比如magic(15) 給你一個15* 15 magic方陣 這將會是一幅 15*15的magic方陣值的圖
最后 總結一下這段視頻 你看到我所做的 是使用逗號連接函數調用 這里是你如何真正做到這一點 如果我鍵入a=1 b=2 c=3 然后按Enter鍵 其實這是將這 三個命令同時執行 或者是 將三個命令一個接一個執行 它將輸出所有這三個結果 這很像 a=1; b=2; c=3; 如果我用分號來代替逗號 沒有輸出出任何東西 所以你知道 這里我們稱之為 逗號連接的命令或函數調用 只是另一種 Octave中更便捷的方式 將多條命令例如imagesc colorbar colormap 將這多條命令寫在同一行中 就是這樣 現在你知道如何繪制 Octave中不同的圖像
在下面的視頻中 下一個主要內容 我將告訴你怎樣在Octave中 寫控制語句 比如if while for語句 並且定義和使用函數
Control Statements: for, while, if statement
在這段視頻中 告訴你怎樣 為你的 Octave 程序寫控制語句 諸如 "for" "while" "if" 這些語句 並且如何定義和使用方程
這是我們的 Octave 窗口 我先告訴你如何使用 “for” 循環 首先 我要將 v 值設為 一個10行1列 的零向量 現在 我要寫一個 “for" 循環 讓 i 等於 1 到 10 寫出來就是 i = 1:10 讓我們來看看 我要設 v(i) 的值 等於 2 的 i 次方 循環最后 結束 (end) 這里的空格沒關系 所以我就加一些空格 讓縮進后的代碼看起來結構更清晰 但是你要知道這里的空格沒有意義 如果按我這樣做 那么 向量 v 的值就是 這樣一個集合 2的一次方 2的二次方 依此類推 於是這就是我的 i 等於 1 到 10 的語句結構 讓 i 遍歷 1 到 10 的值
>> v=zeros(10,1); >> for i=1:10, v(i) = 2^i; end; >> v v = 2 4 8 16 32 64 128 256 512 1024
另外 你還可以通過 設置你的 indices (索引) 等於 1 一直到10 來做到這一點 這時 indices 就是一個從1到10的序列 你也可以寫 i = indices 這實際上和我直接把 i 寫到 1 到 10 是一樣 你可以寫 disp(i) 也能得到一樣的結果
>> for i=indices, disp(i); end; 1 2 3 4 5 6 7 8 9 10
所以 這就是一個 “for” 循環 如果你對 “break” 和 “continue” 語句比較熟悉 Octave里也有 “break” 和 “continue” 語句 你也可以在 Octave環境里 使用那些循環語句
但是首先讓我告訴你 一個 while 循環是如何工作的 這是我的 v 向量 讓我們寫個 while 循環 i = 1 ; while i <= 5 ; 讓我們設置 v(i) 等於 100 然后 i 加 1 結束 (end) 所以這是什么意思呢 我讓 i 取值從 1 開始 然后我要 讓 v(i) 等於 100 再讓 i 遞增 1 直到 i 大於 5停止
>> i = 1; >> while i <= 5, v(i) = 100; i = i+1; end; >> v v = 100 100 100 100 100 64 128 256 512 1024
現在來看一下結果 原來的向量 v 是2的這些次方 我現在已經取出了 向量的前五個元素 把他們用100覆蓋掉 這就是一個while循環的句法結構
現在我們來分析另外一個例子 i = 1; while true, 這里我將向你展示 如何使用break語句 比方說 v(i) = 999 然后讓 i = i+1 當 i 等於6的時候 break (停止循環) 結束 (end) 當然這也是我們第一次 使用一個 if 語句 所以 我希望你們可以理解這個邏輯 讓 i 等於1 然后開始下面的增量循環 while語句重復設置 v(i) 等於1 (此處口誤 應為999 譯者注) 不斷讓i增加 然后當 i 達到6 做一個 中止循環的命令 盡管有while循環 語句也就此中止 所以最后的效果是 取出向量 v 的前5個元素 並且把它們設置為999 然后運行 的確如此 我們用999覆蓋了 v 的前五個元素 所以 這就是 if 語句和 while 語句的句法結構 並且要注意 要有end 這里是有兩個 end 的 這里的 end 結束的是 if 語句 第二個 end 結束的是 while 語句
>> i=1; >> while true, v(i) = 999; i = i+1; if i == 6, break; end; end; >> v v = 999 999 999 999 999 64 128 256 512 1024
現在讓我告訴你使用 if-else 語句時 更一般的句法結構 舉個例子 v(1) 等於999 假設我們 令 v(1) 等於2 所以 讓我輸入 if v(1) == 1, disp('The value is one'); 這里出現了一個else語句 或者更確切地說 這里是一個 elseif語句 elseif v(1) == 2, 這就是說 如果這種情況下命題為真 執行 disp('The value is two'); 否則(else) 執行 disp('The value is not one or two'); 好了 這就是一個if-else語句 if-else語句 記得最后有end 當然了 我們剛剛設置過 v(1)等於2 所以顯然 顯示的是 "The value is two"
>> v(1) ans = 999 >> v(1) = 2; >> if v(1)==1, disp('The value is one'); elseif v(1) == 2, disp('The value is two'); else disp('The value is not one or two'); end;
最后 我覺得現在 提醒一件事 如果你需要退出 Octave 你可以鍵入 exit 命令然后 回車就會退出 Octave 或者命令 ‘quit’ 也可以
最后 讓我們來說說 函數 (functions) 如何定義和調用函數 這是我的桌面 我在桌面上存了一個 預先定義的文件 名為 “squarethisnumber.m” 這就是在 Octave 環境下定義的函數 你需要創建一個文件 用你的函數名來命名 然后以 .m 的后綴結尾 當 Octave 發現這文件 它知道應該在什么位置 尋找 squareThisNumber.m 這個函數的定義 讓我們打開這個文件 請注意 我使用的是 微軟的寫字板程序來打開這個文件 我只是想建議你 如果你也使用微軟的 Windows 系統 那么可以使用寫字板程序 而不是記事本 來打開這些文件 如果你有別的什么 文本編輯器 那也可以 但記事本有時會把代碼的間距弄得很亂 如果你只有記事本程序 那也能用 但最好是 如果你有寫字板的話 我建議你用寫字板 或者其他可以編輯函數的文本編輯器 現在我們來說如何在 Octave 里定義函數 我們先來放大一點 這個文件只有三行 第一行寫着 function y = squareThisNumber(x) 這就告訴 Octave 我想返回一個 y 值 我想返回一個值 並且返回的這個值 將被存放於 變量 y 里 另外 它告訴了 Octave 這個函數有一個參數 就是參數 x 還有定義的函數體 也就是 y 等於 x 的平方
function y = squareThisNumber(x) y = x^2;
現在讓我們嘗試調用這個函數 SquareThisNumber(5) 這實際上 是行不通的 Octave 說這個方程未被定義 這是因為 Octave 不知道在哪里找這個文件 所以像之前一樣 我們使用 pwd 現在不在我的目錄下 因此我們把路徑設為 "C:\User\ang\desktop" 這就是我的桌面的路徑 噢 打錯了 應該是 "Users" 現在如果我 鍵入SquareThisNumber(5) 返回值是25
cd 'C:\Users\ang\desktop' pwd
還有一種更高級的功能 這只是對那些知道 “search path (搜索路徑)” 這個術語的人使用的 所以如果你 想要修改 Octave 的搜索路徑 你可以把下面這部分 作為一個進階知識 或者選學材料 僅適用於那些 熟悉編程語言中 搜索路徑概念的同學 你可以使用 addpath 命令添加路徑 添加路徑 “C:\Users\ang\desktop” 將該目錄添加到 Octave 的搜索路徑 這樣即使你跑到 其他路徑底下 Octave依然知道 會在 Users\ang\desktop 目錄下尋找函數 這樣 即使我現在 在不同的目錄下 它仍然 知道在哪里可以找到 “SquareThisNumber” 這個函數 明白嗎?
addpath('C:\Users\ang\desktop')
但是 如果你不熟悉 搜索路徑的概念 不用擔心 只要確保 在執行函數之前 先用 cd 命令 設置到你函數所在的目錄下 實際上也是一樣的效果
Octave 還有一個 其他許多編程語言都沒有的概念 那就是它可以 允許你定義一個函數 使得返回值是多個值或多個參數 這是一個例子 定義一個函數叫 “SquareAndCubeThisNumber(x)” (x的平方以及x的立方) 這說的就是 函數返回值是兩個 y1 和 y2 接下來就是 y1是被平方后的數 y2是被立方后的結果 這就是說 函數會真的返回2個值
function [y1, y2] = squareAndCubeThisNumber(x) y1 = x^2; y2 = x^3;
所以 有些同學可能會根據 你使用的編程語言 比如你們可能熟悉的C或C++ 通常情況下 認為作為函數返回值只能是一個值 但 Octave 的語法結構就不一樣 可以返回多個值 現在回到 Octave 窗口 如果我鍵入 = SquareAndCubeThisNumber(5) 然后 a 就等於25 b 就等於 5的立方 125 所以說如果你需要定義一個函數 並且返回多個值 這一點常常會帶來很多方便
最后 我來給大家演示一下 一個更復雜一點的函數的例子 比方說 我有一個數據集 像這樣 數據點為, , 我想做的事是 定義一個 Octave 函數來計算代價函數 J(θ) 就是計算 不同 θ 值所對應的代價函數值 J 首先讓我們把數據放到 Octave 里 我把我的矩陣設置為 X = ; 這就是我的設計矩陣 X 第一列表示x0項 矩陣的第一列 第二列表示 我的三個訓練樣本的 x 值 現在我再來 設置 y 值為 就像這樣 是y軸對應值 現在我們設定 theta 為
>> x = [1 1; 1 2; 1 3]; >> y = [1; 2; 3]; >> theta = [0;1];
現在我的桌面上 已經有我預定義的代價函數 J 如果我打開函數 函數的定義應該是下面這樣的 所以 函數J 就寫成 J = costFunctionJ(X, y, theta) 這里有一些注釋 主要用於解釋輸入變量 接下來幾步 設定 m 為訓練樣本的數量 也就是 X 的行數 計算預測值 predictions 預測值等於 X 乘以 theta 這里是注釋行 是上一個注釋行拐過來的部分 下面就是計算平方誤差 公式就是 預測值減去 y 值 然后取出來每一項進行平方 最后就可以 計算代價函數 J 並且 Octave 知道 J 是一個我想返回的值 因為 J 出現在了我函數的定義里
function J = costFunctionJ(X,y,theta) m = size(X,1); predictions = X*theta; sqrErrors = (predictions-y).^2; J = 1/(2*m) * sum(sqrErrors);
另外 你可以隨時 暫停一下視頻 如果你想 仔細看一下這個函數的定義 確保你明白了定義中的每一步 現在當我 在 Octave 里運行時 我鍵入 j = costFunctionJ(x, y, theta) 然后他就開始計算 噢 又打錯了 這里應該是大寫 X 它就計算出 j 等於0 這是因為 如果我的數據集 x 為 y 也為 然后設置 θ0 等於0 θ1 等於1 這給了我恰好45度的斜線 這條線是可以完美擬合我的數據集的
>> j = costFunctionJ(X,y,theta)
而相反地 如果我設置 theta 等於 那么這個假設就是 0是所有的預測值 和剛才一樣 設置θ0 = 0 θ1 也等於0 然后我計算的代價函數 結果是2.333 實際上 他就等於1的平方 也就是第一個樣本的平方誤差 加上2的平方 加上3的平方 然后除以2m 也就是訓練樣本數的兩倍 這就是2.33 因此這也反過來驗證了 我們這里的函數 計算出了正確的代價函數 這些就是我們 用簡單的訓練樣本 嘗試的幾次試驗 這也可以作為我們對 定義的代價函數 J 進行了完整性檢查 確實是可以計算出正確的代價函數的 至少基於這里的 X 和 y 是成立的 也就是我們 這幾個簡單的訓練集 至少是成立的
好啦 現在你知道 如何在 Octave 環境下寫出正確的控制語句 比如 for 循環、while 循環和 if 語句 以及如何定義和使用函數
在接下來的視頻中 我會非常快的 介紹一下 如何在這門課里 完成和提交作業 如何使用我們的提交系統 在此之后 在最后的 Octave 教程視頻里 我會講解一下向量化 這是一種可以使你的 Octave 程序運行非常快的思想
Vectorization向量化
在這段視頻中 我將介紹有關向量化的內容 無論你是用Octave 還是別的語言 比如MATLAB 或者你正在用Python NumPy 或 Java C C++ 所有這些語言都具有各種線性代數庫 這些庫文件都是內置的 容易閱讀和獲取 他們通常寫得很好 已經經過高度優化 通常是數值計算方面的博士 或者專業人士開發的 而當你實現機器學習算法時 如果你能 好好利用這些 線性代數庫或者說 數值線性代數庫 並聯合調用它們 而不是自己去做那些 函數庫可以做的事情 如果是這樣的話 那么 通常你會發現 首先 這樣更有效 也就是說運行速度更快 並且更好地利用 你的計算機里可能有的一些並行硬件系統 等等 第二 這也意味着 你可以用更少的代碼來實現你需要的功能 因此 實現的方式更簡單 代碼出現問題的有可能性也就越小
舉個具體的例子 與其自己寫代碼 做矩陣乘法 如果你只在Octave中 輸入 a乘以b 就是一個非常有效的 兩個矩陣相乘的程序 有很多例子可以說明 如果你用合適的向量化方法來實現 你就會有一個簡單得多 也有效得多的代碼
讓我們來看一些例子 這是一個常見的線性回歸假設函數 如果 你想要計算 h(x) 注意到右邊是求和 那么你可以 自己計算 j =0 到 j = n 的和 但換另一種方式來想想 是把 h(x) 看作 θ 轉置乘以 x 那么 你就可以寫成 兩個向量的內積 其中 θ 就是 θ0 θ1 θ2 如果你有兩個特征量 如果 n 等於2 並且如果 你把 x 看作 x0 x1 x2 這兩種思考角度 會給你兩種不同的實現方式
比如說 這是未向量化的代碼實現方式 計算 h(x) 是未向量化的 我的意思是 沒有被向量化 我們可能首先要初始化變量 prediction 的值為0.0 而這個 變量 prediction 的最終結果就是 h(x) 然后 我要用一個 for 循環 j 取值 0 到 n+1 變量prediction 每次就通過 自身加上 theta(j) 乘以 x(j) 更新值 這個就是算法的代碼實現 順便我要提醒一下 這里的向量 我用的下標是 0 所以我有 θ0 θ1 θ2 但因為 MATLAB 的下標從1開始 在 MATLAB 中 θ0 我們可能會 用 theta(1) 來表示 這第二個元素 最后就會變成 theta(2) 而第三個元素 最終可能就用 theta(3) 表示 因為 MATLAB 中的下標從1開始 即使我們實際的 θ 和 x 的下標從0開始 這就是為什么 這里我的 for 循環 j 取值從 1 直到 n+1 而不是 從 0 到 n 清楚了嗎? 但這是一個 未向量化的代碼實現方式 我們用一個 for 循環 對 n 個元素進行加和
作為比較 接下來是 向量化的代碼實現 你把 x 和 θ 看做向量 而你只需要 令變量 prediction 等於 theta轉置 乘以 x 你就可以這樣計算 與其寫所有這些 for 循環的代碼 你只需要一行代碼 這行代碼 右邊所做的 就是 利用 Octave 的高度優化的數值 線性代數算法來計算 兩個向量的內積 θ 以及 x 這樣向量化的實現不僅僅是更簡單 它運行起來也將更加高效
這就是 Octave 所做的 而向量化的方法 在其他編程語言中同樣可以實現 讓我們來看一個 C++ 的例子 這就是未向量化的代碼實現的樣子 我們再次初始化變量 prediction 為 0.0 然后我們現在有一個完整的 從 j 等於 0 直到 n 變量 prediction += theta 乘以 x 再一次 你有這樣的自己寫的 for 循環 與此相反 使用一個比較好的 C++ 數值線性代數庫 你就可以用這個方程 來寫這個函數 與此相反 使用較好的 C++ 數值線性代數庫 你可以寫出 像這樣的代碼 因此取決於你的 數值線性代數庫的內容 你可以有一個對象 (object) 像這個 C++ 對象 theta 和一個 C++ 對象 向量 x 你只需要用 theta.transpose ( ) 乘以 x 而這次是讓 C++ 來實現運算 因此 你只需要在 C++ 中將兩個向量相乘 根據你所使用的 數值和線性代數庫的使用細節的不同 你最終使用的代碼表達方式 可能會有些許不同 但是通過 一個庫來做內積 你可以得到一段更簡單 更有效的代碼
現在 讓我們來看一個更為復雜的例子 提醒一下 這是線性回歸算法梯度下降的更新規則 所以 我們用這條規則對 j 等於 0 1 2 等等的所有值 更新 對象 θj 我只是 用 θ0 θ1 θ2 來寫方程 那就是假設我們有兩個特征量 所以 n等於2 這些都是我們需要對 θ0 θ1 θ2 進行更新 你可能還記得 在以前的視頻中說過 這些都應該是同步更新
因此 讓我們來看看 我們是否可以拿出一個 向量化的代碼實現 這里是和之前相同的三個方程 只不過寫得小一點而已 你可以想象 實現這三個方程的方式之一 就是用 一個 for 循環 就是讓 j 等於0 等於 等於2 來更新 θj 但讓我們 用向量化的方式來實現 看看我們是否能夠有一個更簡單的方法 基本上用三行代碼 或者一個 for 循環 一次實現這三個方程 讓我們來看看怎樣能用這三步 並將它們壓縮成 一行向量化的代碼來實現 做法如下 我打算 把 θ 看做一個向量 然后我用 θ 減去 α 乘以 某個別的向量 δ 來更新 θ 這里的 δ 等於 m 分之 1 對 i=1 到 m 進行求和 然后這個表達式 對吧? 讓我解釋一下是怎么回事 在這里 我要把 θ 看作一個向量 有一個 n+1 維向量 我是說 θ 被更新 我們的 n+1 維向量 α 是一個實數 δ 在這里 是一個向量 所以這個減法運算是一個向量減法 沒問題吧 ? 因為 α 乘以 δ 是一個向量 所以 θ 就是 θ 減去 α 乘以 δ 得到的向量 那么什么是向量 δ 呢 ? 嗯 向量 δ 是這樣子的 這部分實際上 代表的就是 這部分內容 具體地說 δ 將成為 n+1 維向量 並且向量的第一個元素 就等於這個 所以我們的 δ 如果要寫下標的話 就是從零開始 δ0 δ1 δ2 我想要的是 δ0 等於 這個 第一行綠色框起來的部分 事實上 你可能會 寫出 δ0 是 m 分之 1 乘以 h(x(i)) 減去 y(i) 乘以 x(i)0 的求和 所以讓我們 在同一頁上 計算真正的 δ δ 就是 m 分之 1 乘以這個和 那這個和是什么 ? 嗯 這一項 是一個實數 這里的第二個項 是 x(i) 這一項是一個向量 對吧 ? 因為 x(i) 可能是一個向量 這將是 x(i)0 x(i)1 x(i)2 對吧 ? 那這個求和是什么 ? 嗯 這個求和就是 這里的式子 這里的這一項 等於 h(x(1)) - y(1) 乘以 x(1) 加上 h(x(2)) - y(2) 乘以 x(2) 依此類推 對吧 ? 因為這是對 i 的加和 所以 當 i 從 1 到 m 你就會得到這些不同的式子 然后作加和 每個式子的意思 很像 如果你還記得實際上 在以前的一個小測驗 如果你要解這個方程 我們說過 為了向量化這段代碼 我們會令 u = 2v +5w 因此 我們說 向量u 等於2乘以向量v 加上 5乘以向量 w 用這個例子說明 如何對不同的向量進行相加 這里的求和是同樣的道理 這一部分 只是一個實數 就有點像數字 2 而這里是別的一些數字 來乘以向量x1 這就像是 2v 只不過用別的數字乘以 x1 然后加上 你知道 不是5w 而是用 別的實數乘以 一個別的向量 然后你 加上 其他的向量 這就是為什么 總體而言 在這里 這整個量 δ 就是一個向量 具體而言 對應這三個 δ 的元素 如果n等於2 δ 的三個元素一一對應 這個 第二個 以及這第三個 式子 這就是為什么 當您更新 θ 值時 根據 θ - αδ 這個式子 我們最終能得到 完全符合最上方更新規則的 同步更新 我知道 幻燈片上的內容很多
但是再次重申 請隨時暫停視頻 我也鼓勵你 一步步對比這兩者的差異 如果你不清楚剛才的內容 我希望你能一步一步讀幻燈片的內容 以確保你理解 為什么這個式子 用 δ 的這個定理 定義的 好嗎 ? 以及它為什么 和最上面的更新方式是等價的 為什么是這樣子的 就是這里的式子 這就是向量 x 而我們只是用了 你知道 這三個計算式並且壓縮 成一個步驟 用這個向量 δ 這就是為什么我們能夠 向量化地實現 線性回歸 所以 我希望 步驟是有邏輯的 請務必看視頻 並且保證 你確實能理解它 如果你實在不能理解 它們數學上等價的原因 你就直接實現這個算法 也是能得到正確答案的 所以即使你沒有 完全理解為何是等價的 如果只是實現這種算法 你仍然能實現線性回歸算法 所以如果你能 弄清楚為什么這兩個步驟是等價的 那我希望你可以對 向量化有一個更好的理解 以及 最后 如果你在實現線性回歸的時候 使用一個或兩個以上的特征量 有時我們使用 幾十或幾百個特征量 來計算線性歸回 當你使用向量化地實現 線性回歸 通常運行速度就會比你以前用 你的 for 循環快的多 也就是自己 寫代碼更新 θ0 θ1 θ2 因此使用向量化實現方式 你應該是能夠得到 一個高效得多的線性回歸算法 而當你向量化 我們將在之后的課程里面學到的算法 這會是一個很好的技巧 無論是對於 Octave 或者 一些其他的語言 如C++ Java 來讓你的代碼運行得更高效