參考: https://blog.csdn.net/iszhenyu/article/details/78712228; 吳恩達機器學習視頻;
在學習機器學習的過程中,免不了要跟MATLAB、Octave打交道,這兩個工具都可以幫助我們很好的解決數值計算問題,兩者的語法也非常接近。
Octave是一個完全開源免費的軟件,無論是Windows還是Mac環境都可以在官網下載安裝包直接安裝,非常方便。
這篇文章主要介紹在學習機器學習的過程中會經常使用到的Octave的一些命令和語法。當然,一篇文章肯定無法覆蓋Octave的所有功能,但是對於我們入門機器學習應該足夠了。
基本計算
Octave中的 加、減、乘、除運算:
>> 2 + 2 ans = 4 >> 3 - 2 ans = 1 >> 5 * 8 ans = 40 >> 1 / 2 ans = 0.50000
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
同時也可以進行平方、立方等指數運算:
>> 2^2 ans = 4 >> 2^3 ans = 8
- 1
- 2
- 3
- 4
在Octave中,我們可以使用符號
%
來進行注解,其后面的同行語句都將不會得到執行。例如:2 + 3 % + 5 輸出的結果為5。如果你熟悉java語言,可以類比為//
,或者是Python中的#
。
邏輯運算
常用的邏輯運算包括:等於(==
)、不等於(~=
)、並(&&
)、或(||
)四種,分別用不同的符號表示。
運算的結果用0、1表示,1表示成立,0表示不成立。
>> 1 == 2 ans = 0 >> 1 == 1 ans = 1 >> 1 ~= 2 ans = 1 >> 1 && 0 ans = 0 >> 1 || 0 ans = 1
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
在Octave中,同時還內置了一些函數來進行邏輯運算,比如異或運算就可以用xor
這個函數來代替:
>> xor(3, 1) ans = 0 >> xor(3, 3) ans = 0 >> xor(1, 0) ans = 1
- 1
- 2
- 3
- 4
- 5
- 6
在Octave中內置了很多的函數,有時,我們可能記不太清某個函數的具體用法,這個時候,Octave給我們提供了
help
命令,通過這個命令可以查看函數的定義以及示例。比如,我們想看下xor
這個函數怎么用,可以輸入:help xor
。
變量
同其他編程語言一樣,我們也可以在Octave中定義變量,語法跟其他語言也比較類似:
>> a = 3 a = 3 >> a = 3; >>
- 1
- 2
- 3
- 4
上面的例子中,我們定義了變量a,並將它賦值為3。
有一個細節需要我們注意的是:在第一次執行a = 3
的后面沒有加;
號,Octave在執行完賦值語句后又打印出了變量a的值。而在第二句中,我們在賦值語句的末尾添加了;
號,這個時候,Octave只會執行賦值語句,將不再打印變量值。
除了將數值賦給一個變量,我們也可以將字符串、常量賦給變量:
>> b = 'hi'; % 因為加了;號,沒有打印出b的值 >> b % 直接輸入變量名稱,即可打印變量值 b = hi >> c = (3 >= 1) c = 1 >> a = pi; >> a a = 3.1416
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
在上面的第二行語句,直接輸入了變量名稱(沒有分號),Octave直接打印出了變量的值。
除此以外,也可以使用disp
函數來完成打印變量值的功能:
>> disp(a) 3.1416
- 1
- 2
結合printf
函數,還能實現格式化打印。還是以上面的變量a為例:
>> disp(sprintf('2 decimals: %0.2f', a)) 2 decimals: 3.14 >> disp(sprintf('6 decimals: %0.6f', a)) 6 decimals: 3.141593
- 1
- 2
- 3
- 4
- 5
printf
函數沿用了C語言的語法格式,所以如果你有學習過C語言的話,對上面的寫法應該會比較熟悉。
除了使用printf
外,利用format long
、format short
也可以指定打印的精度,在Octave中,short
是默認的精度:
octave:32> format long octave:33> a a = 3.14159265358979 octave:34> format short octave:35> a a = 3.1416
- 1
- 2
- 3
- 4
- 5
- 6
- 7
向量和矩陣
向量/矩陣的生成
在Octave中可以這樣定義矩陣:將矩陣的元素按行依次排列,並用[]
包裹,矩陣的每一行用;
分割。
下面定義了一個3×2的矩陣A
>> A = [1 2; 3 4; 5 6] A = 1 2 3 4 5 6
- 1
- 2
- 3
- 4
- 5
說明:
;
號在這里的作用可以看做是換行符,也就是生成矩陣的下一行。
在命令行下,也可以將矩陣的每一行分開來寫:
>> A = [1 2; > 3 4; > 5 6] A = 1 2 3 4 5 6
- 1
- 2
- 3
- 4
- 5
- 6
- 7
向量的創建與矩陣類似:
>> V1 = [1 2 3] V1 = 1 2 3 >> V2 = [1; 2; 3] V2 = 1 2 3
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
在上面的例子中,V1是一個行向量,V2是一個列向量。
其他一些寫法:
>> V = 1: 0.2: 2 V = 1.0000 1.2000 1.4000 1.6000 1.8000 2.0000
- 1
- 2
- 3
上面的寫法可以快速生成行向量,1 為起始值,0.2為每次遞增值,2為結束值,我們也可以省略0.2,那么就會生成遞增為1的行向量:
>> v = 1:5 v = 1 2 3 4 5
- 1
- 2
- 3
同樣,我們也可以利用Octave內置的函數來生成矩陣,比較常用的幾個函數是ones
、zeros
、rand
、eye
。
ones(m, n)
函數生成一個m行n列的矩陣,矩陣中每個元的值為1。
zeros(m, n)
函數生成一個m行n列的矩陣,矩陣中每個元的值為0。
rand(m, n)
函數生成一個m行n列的矩陣,矩陣的每個元是0到1之間的一個隨機數。
eye(m)
函數生成一個大小為m的單位矩陣。
>> ones(2, 3) ans = 1 1 1 1 1 1 >> w = zeros(1, 3) w = 0 0 0 >> w = rand(1, 3) w = 0.19402 0.23458 0.49843 >> eye(4) ans = Diagonal Matrix 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0 1
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
向量/矩陣的屬性
在說明矩陣的屬性操作之前,我們先來定義一個矩陣A:
>> A
A =
1 2 3 4 5 6
- 1
- 2
- 3
- 4
- 5
矩陣有了,怎么知道一個矩陣的大小呢?在Octave中,內置了size
函數。
size
函數返回的結果也是一個矩陣,但這個矩陣的大小是1×2,這個1×2的矩陣中,兩個元素的值分別代表了參數矩陣的行數和列數。
>> sa = size(A); >> sa sa = 3 2 >> size(sa) ans = 1 2
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
當然,我們也可以只獲取矩陣的行數或列數,使用的同樣是size
函數,唯一不同的是需要多指定一個參數,來標識想獲取的是行還是列,這個標識用1或2來表示,1代表想獲取的是行數,2代表想獲取的是列數:
>> size(A, 1) ans = 3 >> size(A, 2) ans = 2
- 1
- 2
- 3
- 4
除了size
函數,另外一個比較常用的是length
函數,它獲取的是矩陣中最大的那個維度的值,也就是說,對於一個m×n的矩陣,return m if m > n else n。
對於向量來說,利用length
可以快速獲取向量的維數:
>> V = [1 2 3 4] V = 1 2 3 4 >> length(V) ans = 4 octave:67> length(A) ans = 3
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
向量/矩陣的運算
我們還是以上一小節定義的矩陣A為例。
獲取矩陣指定行指定列的元素,注意這里的行、列都是從1開始的,比如獲取矩陣A的第3行第2列元素:
>> A(3, 2) ans = 6
- 1
- 2
也可以獲取矩陣整行或整列的元素,某行或某列的全部元素可以用 :
號代替,返回的結果就是一個行向量或一個列向量:
>> A(3,:) ans = 5 6 >> A(:, 2) ans = 2 4 6
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
更一般情況,我們也可以指定要獲取的某幾行或某幾列的元素:
>> A([1, 3],:) ans = 1 2 5 6 >> A(:,[2]) ans = 2 4 6
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
除了獲取矩陣元素,我們也可以給矩陣的元素重新賦值。可以給指定行指定列的某一個元素賦值,也可以同時給某行或某列的全部元素一次性賦值:
>> A(:,2) = [10, 11, 12] A = 1 10 3 11 5 12 >> A(1,:) = [11 22] A = 11 22 3 4 5 6
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
有的時候,我們還需要對矩陣進行擴展,比如增廣矩陣,要在矩陣的右側附上一個列向量:
>> A = [A, [100; 101; 102]] A = 1 2 100 3 4 101 5 6 102
- 1
- 2
- 3
- 4
- 5
上面第一句中,
,
號也可以省略,只使用空格也是一樣的效果。這樣,那行賦值語句就變成這樣:A = [A [100; 101; 102]]
兩個矩陣也可以進行組合:
>> A = [1 2; 3 4; 5 6] A = 1 2 3 4 5 6 >> B = [11 12; 13 14; 15 16] B = 11 12 13 14 15 16 >> [A B] ans = 1 2 11 12 3 4 13 14 5 6 15 16 >> [A; B] ans = 1 2 3 4 5 6 11 12 13 14 15 16
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
我們也可以將矩陣的每一列組合在一起,轉為一個更大的列向量:
>> A(:)
ans = 1 3 5 2 4 6
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
接下來,為了說明矩陣與矩陣的運算,我們先來定義三個矩陣:
>> A
A =
1 2 3 4 5 6 >> B B = 11 12 13 14 15 16 >> C C = 1 1 2 2
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
矩陣的相乘:
>> A*C
ans = 5 5 11 11 17 17
- 1
- 2
- 3
- 4
- 5
矩陣A的各個元素分別乘以矩陣B對應元素:
>> A .* B
ans = 11 24 39 56 75 96
- 1
- 2
- 3
- 4
- 5
點運算在這里可以理解為是對矩陣中每個元素做運算。比如,下面的例子就是對A中每個元素做平方,用1分別去除矩陣中的每個元素:
>> A .^ 2 ans = 1 4 9 16 25 36 >> 1 ./ [1; 2; 3] ans = 1.00000 0.50000 0.33333
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
有一種特殊情況是,當一個實數與矩陣做乘法運算時,我們可以省略.
直接使用*
即可:
>> -1 * [1; -2; 3] % 也可以簡寫為 -1[1; 2; 3] ans = -1 2 -3
- 1
- 2
- 3
- 4
- 5
除此以外,Octave中內置的一些函數也是針對每個元素做運算的,比如對數運算、指數運算和絕對值運算等:
octave:50> log([1; 2; 3]) ans = 0.00000 0.69315 1.09861 octave:51> exp([1; 2; 3]) ans = 2.7183 7.3891 20.0855 octave:53> abs([1; -2; 3]) ans = 1 2 3
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
矩陣的加法、轉秩和逆:
>> V + ones(length(V), 1) % V = [1; 2; 3] ans = 2 3 4 % 矩陣的轉秩 >> A' ans = 1 3 5 2 4 6 % 求矩陣的逆 >> pinv(A) ans = 0.147222 -0.144444 0.063889 -0.061111 0.022222 0.105556 -0.019444 0.188889 -0.102778
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
其他一些運算:
% a = [1 15 2 0.5],求最大值 >> val = max(a) val = 15 % 求最大值,並返回最大值的索引 >> [val, idx] = max(a) val = 15 idx = 2 % 矩陣對應元素的邏輯運算 >> a <= 1 ans = 1 0 0 1 >> find(a < 3) ans = 1 3 4 % 計算之和 >> sum(a) ans = 18.500 % 計算乘積 >> prod(a) ans = 15 % 向下取整 >> floor(a) ans = 1 15 2 0 % 向上取整 >> ceil(a) ans = 1 15 2 1 % 生成一個隨機矩陣,矩陣元素的值位於0-1之間 >> rand(3) ans = 0.458095 0.323431 0.648822 0.481643 0.789336 0.559604 0.078219 0.710996 0.797278 % 矩陣按行上下對換 >> flipud(eye(4)) ans = Permutation Matrix 0 0 0 1 0 0 1 0 0 1 0 0 1 0 0 0
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
控制語句和函數
for、while、if 語句
octave中for,while,if語句的使用方式和c語言一樣,不同的是大括號的功能是通過end實現的,下面例子中空格沒有任何作用,只是起到視覺上清晰的作用。
首先我們定義一個列向量:V = zeros(10, 1),然后通過 for
循環語句來更新向量V中的每一個元素:
>> for i=1:10, V(i) = 2^i; end; >> V V = 2 4 8 16 32 64 128 256 512 1024
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
或者,我們也可以換一種寫法:
>> indices = 1:10; >> indices indices = 1 2 3 4 5 6 7 8 9 10 >> for i=indices, disp(i); end; 1 2 3 4 5 6 7 8 9 10
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
每一個 for
循環都是用 end
來結尾,固定寫法,記住就好。
下面看while
語句:
>> i = 1; >> while i <= 5, disp(V(i)); i = i+1; end; 2 4 8 16 32 >> i = 1; >> while true, disp(V(i)); if i > 5, break; end; i = i + 1; end; 2 4 8 16 32 64
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
while
和 if
語句同樣需要使用 end
來表示完結,同時,在 for
或 while
中,我們也可以使用 break
關鍵詞來提前退出循環。
函數
我們還是先看例子,然后再說明具體的寫法:
>> function y = squareNum(x) y = x^2; end; >> squareNum(3) ans = 9
- 1
- 2
- 3
- 4
- 5
在Octave中,定義一個函數需要使用function
關鍵字,然后緊跟在 function 后面的是函數的聲明,包括返回值,函數名稱和參數,之后換行來實現具體的函數功能。
Octave的函數不需要顯示的返回語句,Octave會將函數第一行聲明的返回值返回給調用方,因此,我們在函數體中只需將最終的計算結果賦給定義的返回值,比如上面例子中的y
。
還有一點需要說明的是,在Octave中,函數可以返回多個值:
>> function [y1, y2] = calVal(x) y1 = x^2; y2 = x^3; end; >> [a, b] = calVal(3) a = 9 b = 27
- 1
- 2
- 3
- 4
- 5
- 6
- 7
也可以把函數寫進文件中,然后加載實現函數。
進入octave后,cd到指定的目錄下,這里我是把函數文件存在d盤下的文件中
cd D:\app2018\octave
之后你可以用pwd打印出當前目錄的路徑看看是否是在該文件下。
在該目錄下新建一個文件名為“squareThisNumber.m”后綴是.m這樣octave可以自動識別,雙擊后就會用notepad++自動打開,就可以編輯自己的函數。
注意:文件名要和函數名保持一致。
function y = squareThisNumber(x)
y = x^2;
函數的返回值是y,函數的自變量是x(這里只有一個返回值,可以有多個返回值),函數的主體是y = x^2
>squareThisNumber(5)
ans = 25
這樣就實現一簡單求數平方的函數。
加載和保存數據
在上面一節中,介紹了如何在Octave的交互環境定義函數。但是大部分時候,我們都會將函數保存在文件中,從而在需要時可以隨時調用。我們也能夠在文件中存儲數據,比如矩陣參數等,使用 load 命令可以將文件中的內容加載進來。
通常會比較常用的一些命令有如下幾個:
顯示當前的工作目錄:
>> pwd
ans = /Users/xiaoz
- 1
- 2
進到指定的目錄:
>> cd octave
>> pwd
ans = /Users/xiaoz/octave
- 1
- 2
- 3
列出當前目錄下的文件:
>> ls
featureX.dat priceY.dat
- 1
- 2
加載當前目錄下的數據(也可以使用load
函數):
>> load featuresX.dat >> load pricesY.dat
- 1
- 2
查看當前工作空間下都有哪些變量:
>> who
Variables in the current scope:
ans featuresX pricesY
- 1
- 2
- 3
查看詳細的變量信息:
>> whos
Variables in the current scope:
Attr Name Size Bytes Class
==== ==== ==== ===== =====
ans 1x13 13 char featuresX 3x2 48 double pricesY 3x1 24 double Total is 22 elements using 85 bytes >> featuresX % 查看加載進來的變量 featuresX = 123 1 456 2 789 3 octave:15> pricesY pricesY = 11 22 33
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
clear 命令可以清除一個變量,需要特別小心的是,如果后面沒有跟具體的變量名,則會清空全部變量:
>> clear ans
- 1
保存數據到指定的文件,它的語法格式是這樣的:
save {file_name} {variables}
>> V = pricesY(1:2) % 獲取第一列的前兩個元素 V = 11 22 % 保存變量V到hello.mat文件 >> save hello.mat V; >> ls featuresX.dat hello.mat pricesY.dat
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
在保存的時候也可以指定一種編碼格式,比如下面的例子指定了 ascii 編碼,如果不指定,數據將會被保存為二進制格式。
>> save hello.txt V -ascii
- 1
有一點需要提示的是:假如你使用
pwd
命令發現當前的工作目錄是A,同時你實現了一個函數someFunc
,存儲在文件someFunc.m
中,如果這個someFunc.m
文件不在A目錄,那么在使用someFunc
函數之前,需要先調用load方法將其加載進來,反之可以直接使用。
繪制圖形
在本篇文章的最后一節,我們來簡單的說下Octave的繪圖能力。
不像其他語言那般繁瑣,Octave中繪圖的接口設計的非常簡潔和直觀,讓你非常容易上手。
我們以繪制一個sin函數曲線和一個cos函數曲線為例,來說明如何在Octave中繪圖。
首先,我們還是先來定義數據
>> t = [0:0.01:0.98]; >> y1 = sin(2*pi*4*t); >> y2 = cos(2*pi*4*t);
- 1
- 2
- 3
這里的t我們看做是橫軸,y1看做是縱軸,然后調用plot
函數
>> plot(t, y1);
- 1
之后會立即在一個新窗口生成我們想要的圖形
接下來我們繼續在這個圖像上繪制cos函數。這時需要用到hold on
命令,它的作用是將新圖像畫在舊圖像上面,而不是覆蓋舊圖像。
為了區分sin函數,我們將cos函數的曲線用紅色標識:
octave:10> hold on; octave:11> plot(t,y2, 'r');
- 1
- 2
這個時候,你看到的圖形應該是這個樣子的:
圖形有了,最后一步就是標明橫軸和縱軸分別代表的含義,再給圖形起一個有意義的名字
>> xlabel('time'); % 指定X軸的名稱 >> ylabel('value'); % 指定Y軸的名稱 >> legend('sin', 'cos'); % 標識第一條曲線是sin,第二條曲線是cos >> title('sin and cos function'); % 給圖片附一個標題
- 1
- 2
- 3
- 4
最終,這個圖形是這樣式的:
如果你願意,還可以將其作為一個圖片保存下來:
octave:16> print -dpng 'sin_cos.png'
- 1
在繪圖中,如果你反悔了,想重新繪圖,怎么辦呢?也很簡單,只要輸入clf
命令,Octave會將繪圖框中的圖形全部清空。
不論何時,輸入close
命令,Octave會關閉該繪圖窗口。
>figure(1);plot(t,y1);
>figure(2);plot(t,y2);
這樣就可以分別用兩個窗口顯示圖像。
>subplot(1,2,1); %這樣做是把窗口分成一個1*2的格子,使用第一個格子;
>plot(t,y1);
>subplot(1,2,2);
>plot(t,y2);
axis(0.5 1 -1 1) %調整右邊圖像的x,y坐標的范圍。
>A = magic(5);
>imagesc(A); %生成一個5*5 的色塊
矢量
有道時候方程向量化,計算起來會更加高效。
A = [a1;a2;a3;........;an]
X = [x1;x2;x3;..........xn]
例子:h(x) = a1x1 + a2x2 + a3x3 + .........+ anxn = AX'(X的轉置);
沒有向量化之前可能會使用for循環的方式實現求和函數,但是轉換成向量來做只需要一條語句就能實現;
p = A * X‘ ;
其實,Octave能做的遠遠不止這些,本篇介紹的這些也不過是冰山一角,但對於我們實踐機器學習的算法已經基本足夠。不要忘記的是,當你對某個函數不清楚的時候,試試help {func name}
。