一篇文章帶你入門Octave
在學習機器學習的過程中,免不了要跟MATLAB、Octave打交道,這兩個工具都可以幫助我們很好的解決數值計算問題,兩者的語法也非常接近。
相對於MLGB o不對,是MATLAB的昂貴,Octave是一個完全開源免費的軟件,無論是Windows還是Mac環境都可以在官網下載安裝包直接安裝,非常方便。
這篇文章主要介紹在學習機器學習的過程中會經常使用到的Octave的一些命令和語法。當然,一篇文章肯定無法覆蓋Octave的所有功能,但是對於我們入門機器學習應該足夠了。
下載,傻瓜式安裝:https://www.gnu.org/software/octave/download.html
一、基本計算
Octave中的 加、減、乘、除運算:
>> 2 + 2
ans = 4
>> 3 - 2
ans = 1
>> 5 * 8
ans = 40
>> 1 / 2
ans = 0.50000
同時也可以進行平方、立方等指數運算:
>> 2^2
ans = 4
>> 2^3
ans = 8
在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
在Octave中,同時還內置了一些函數來進行邏輯運算,比如或運算就可以用xor這個函數來代替:
>> xor(3, 1)
ans = 0
>> xor(3, 3)
ans = 0
>> xor(1, 0)
ans = 1
在Octave中內置了很多的函數,有時,我們可能記不太清某個函數的具體用法,這個時候,Octave給我們提供了 help 命令,通過這個命令可以查看函數的定義以及示例。比如,我們想看下xor這個函數怎麼用,可以輸入:help xor。
三、變量
同其他編程語言一樣,我們也可以在Octave中定義變量,語法跟其他語言也比較類似:
>> a = 3
a = 3
>> a = 3;
>>
上面的例子中,我們定義了變量a,並將它賦值爲3。
有一個細節需要我們注意的是:在第一次執行a = 3的後面沒有加;號,Octave在執行完賦值語句後又打印出了變量a的值。而在第二句中,我們在賦值語句的末尾添加了;號,這個時候,Octave只會執行賦值語句,將不再打印變量值。
除了將數值賦給一個變量,我們也可以將字符串、常量賦給變量:
>> b = 'hi'; % 因爲加了;號,沒有打印出b的值
>> b % 直接輸入變量名稱,即可打印變量值
b = hi
>> c = (3 >= 1)
c = 1
>> a = pi;
>> a
a = 3.1416
除此以外,也可以使用disp函數來完成打印變量值的功能:
>> disp(a)
3.1416
結合printf函數,還能實現格式化打印。還是以上面的變量a爲例:
>> disp(sprintf('2 decimals: %0.2f', a))
2 decimals: 3.14
>> disp(sprintf('6 decimals: %0.6f', a))
6 decimals: 3.141593
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
四、向量和矩陣
向量/矩陣的生成
在Octave中可以這樣定義矩陣:將矩陣的元素按行依次排列,並用[]包裹,矩陣的每一行用;分割。
下面定義了一個3×2的矩陣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
向量的創建與矩陣類似:
>> V1 = [1 2 3]
V1 =
1 2 3
>> V2 = [1; 2; 3]
V2 =
1
2
3
在上面的例子中,V1是一個行向量,V2是一個列向量。
其他一些寫法:
>> V = 1: 0.2: 2
V =
1.0000 1.2000 1.4000 1.6000 1.8000 2.0000
上面的寫法可以快速生成行向量,1 爲起始值,0.2爲每次遞增值,2爲結束值,我們也可以省略0.2,那麼就會生成遞增爲1的行向量:
>> v = 1:5
v =
1 2 3 4 5
同樣,我們也可以利用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
向量/矩陣的屬性
在說明矩陣的屬性操作之前,我們先來定義一個矩陣A:
>> A
A =
1 2
3 4
5 6
矩陣有了,怎麼知道一個矩陣的大小呢?在Octave中,內置了size函數。
size函數返回的結果也是一個矩陣,但這個矩陣的大小是1×2,這個1×2的矩陣中,兩個元素的值分別代表了參數矩陣的行數和列數。
>> sa = size(A);
>> sa
sa =
3 2
>> size(sa)
ans =
1 2
當然,我們也可以只獲取矩陣的行數或列數,使用的同樣是size函數,唯一不同的是需要多指定一個參數,來標識想獲取的是行還是列,這個標識用1或2來表示,1代表想獲取的是行數,2代表想獲取的是列數:
>> size(A, 1)
ans = 3
>> size(A, 2)
ans = 2
除了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
向量/矩陣的運算
我們還是以上一小節定義的矩陣A爲例。
獲取矩陣指定行指定列的元素,注意這裏的行、列都是從1開始的,比如獲取矩陣A的第3行第2列元素:
A(3, 2)
ans = 6
1
2
也可以獲取矩陣整行或整列的元素,某行或某列的全部元素可以用 : 號代替,返回的結果就是一個行向量或一個列向量:
>> A(3,:)
ans =
5 6
>> A(:, 2)
ans =
2
4
6
更一般情況,我們也可以指定要獲取的某幾行或某幾列的元素:
>> A([1, 3],:)
ans =
1 2
5 6
>> A(:,[2])
ans =
2
4
6
除了獲取矩陣元素,我們也可以給矩陣的元素重新賦值。可以給指定行指定列的某一個元素賦值,也可以同時給某行或某列的全部元素一次性賦值:
>> A(:,2) = [10, 11, 12]
A =
1 10
3 11
5 12
>> A(1,:) = [11 22]
A =
11 22
3 4
5 6
有的時候,我們還需要對矩陣進行擴展,比如增廣矩陣,要在矩陣的右側附上一個列向量:
>> A = [A, [100; 101; 102]]
A =
1 2 100
3 4 101
5 6 102
上面第一句中,, 號也可以省略,只使用空格也是一樣的效果。這樣,那行賦值語句就變成這樣: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
我們也可以將矩陣的每一列組合在一起,轉爲一個更大的列向量:
>> A(:)
ans =
1
3
5
2
4
6
接下來,爲了說明矩陣與矩陣的運算,我們先來定義三個矩陣:
>> A
A =
1 2
3 4
5 6
>> B
B =
11 12
13 14
15 16
>> C
C =
1 1
2 2
矩陣的相乘:
>> A*C
ans =
5 5
11 11
17 17
矩陣A的各個元素分別乘以矩陣B對應元素:
>> A .* B
ans =
11 24
39 56
75 96
點運算在這裏可以理解爲是對矩陣中每個元素做運算。比如,下面的例子就是對A中每個元素做平方,用1分別去除矩陣中的每個元素:
>> A .^ 2
ans =
1 4
9 16
25 36
>> 1 ./ [1; 2; 3]
ans =
1.00000
0.50000
0.33333
有一種特殊情況是,當一個實數與矩陣做乘法運算時,我們可以省略.直接使用*即可:
>> -1 * [1; -2; 3] % 也可以簡寫爲 -1[1; 2; 3]
ans =
-1
2
-3
除此以外,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
矩陣的加法、轉秩和逆:
+ 加法
>> 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
其他一些運算:
% 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
五、控制語句和函數
for、while、if 語句
首先我們定義一個列向量: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
# 或者,我們也可以換一種寫法:
>> indices = 1:10;
>> indices
indices =
1 2 3 4 5 6 7 8 9 10
>> for i=indices,
disp(i);
end;
每一個 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
while
和 if
語句同樣需要使用 end 來表示完結,同時,在 for
或 while
中,我們也可以使用 break
關鍵詞來提前退出循環。
函數
我們還是先看例子,然後再說明具體的寫法:
>> function y = squareNum(x)
y = x^2;
end;
>> squareNum(3)
ans = 9
在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
六、加載和保存數據
在上面一節中,介紹瞭如何在Octave的交互環境定義函數。但是大部分時候,我們都會將函數保存在文件中,從而在需要時可以隨時調用。我們也能夠在文件中存儲數據,比如矩陣參數等,使用 load 命令可以將文件中的內容加載進來。
通常會比較常用的一些命令有如下幾個:
顯示當前的工作目錄:
>> pwd
ans = /Users/xiaoz
進到指定的目錄:
>> cd octave
>> pwd
ans = /Users/xiaoz/octave
列出當前目錄下的文件:
>> ls
featureX.dat priceY.dat
加載當前目錄下的數據(也可以使用load函數):
>> load featuresX.dat
>> load pricesY.dat
查看當前工作空間下都有哪些變量:
>> who
Variables in the current scope:
ans featuresX pricesY
查看詳細的變量信息:
>> 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
clear 命令可以清除一個變量,需要特別小心的是,如果後面沒有跟具體的變量名,則會清空全部變量:
>> clear ans
保存數據到指定的文件,它的語法格式是這樣的:
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
在保存的時候也可以指定一種編碼格式,比如下面的例子指定了 ascii 編碼,如果不指定,數據將會被保存爲二進制格式。
>> save hello.txt V -ascii
有一點需要提示的是:假如你使用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);
# t是橫軸,y1是縱軸,然後調用plot函數
>> plot(t, y1);
# 之後會立即在一個新窗口生成我們想要的圖形
接下來我們繼續在這個圖像上繪製cos函數。這時需要用到hold on命令,它的作用是將新圖像畫在舊圖像上面,而不是覆蓋舊圖像。
爲了區分sin函數,我們將cos函數的曲線用紅色標識:
octave:10> hold on;
octave:11> plot(t,y2, 'r');
圖形有了,最後一步就是標明橫軸和縱軸分別代表的含義,再給圖形起一個有意義的名字
>> xlabel('time'); % 指定X軸的名稱
>> ylabel('value'); % 指定Y軸的名稱
>> legend('sin', 'cos'); % 標識第一條曲線是sin,第二條曲線是cos
>> title('sin and cos function'); % 給圖片附一個標題
如果你願意,還可以將其作爲一個圖片保存下來:
octave:16> print -dpng 'sin_cos.png'
在繪圖中,如果你反悔了,想重新繪圖,怎麼辦呢?也很簡單,只要輸入clf命令,Octave會將繪圖框中的圖形全部清空。
不論何時,輸入close命令,Octave會關閉該繪圖窗口。
其實,Octave能做的遠遠不止這些,本篇介紹的這些也不過是冰山一角,但對於我們實踐機器學習的算法已經基本足夠。不要忘記的是,當你對某個函數不清楚的時候,試試help {func name}。