老狗——一篇文章帶你入門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 longformat 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

whileif 語句同樣需要使用 end 來表示完結,同時,在 forwhile 中,我們也可以使用 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}。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章