R Learning Notes: Loop-and-Condition, Function

此篇僅做記錄用,本文所有文字內容,皆來自:
R語言翻轉教室


迴圈是一種寫程式時很泛用的技巧,可以幫助我們用 簡短的程式碼來執行重複的動作。

在做機器學習時,有些演算法是透過不停的重複做一樣的計算,而每次計算都能把學習的結果再改善一些。

這類的動作就很適合用迴圈實做。


假設我們要找 出一個數x ,使得f(x) =x² 的值最小。

x0 <-3
x0² [1]9

接著,我們把`x0–0.2 * x0` 存到變數x1

x1 <- x0–0.2 * x0

事實上,數學可以證明如果我們把`x1–0.2 * x1`存到`x2`、 `x2–0.2 * x2`存到`x3`…以此類推,x1、x2、x3… 就會越來越 靠近最終的答案,也就是0


讓我們用迴圈來驗證這件事。

首先,假定我們要重複算99次。

x <- c(1:100)

請把3 存到`x` 的第一個element

x[1] <- 3

請把`x[1] — 0.2 * x[1]` 存到x 的第二個element

x[2] <- x[1] — 0.2 * x[1]

類似`x[1] — 0.2 * x[1]`的運算會 一直重複。

差別只在於`x[1] — 0.2 *x[1]`中的`x[1]`需要做 改變。

其實`x[1]`在這裡,可以看成是`x[1] — 0.2 * x[1]`的運算 的輸入位置,是x 的第一個element。

理解了這一點之後,為了讓R 可以在改變`x[1]` 的情形下重複`x[1] — 0.2 * x[1]`

這樣的運算, 我們可以利用一個變數`i` 來代表現在要運算的位置。

i <- 1


接著,我們可以把`x[2] <- x[1] — 0.2 * x[1]`這樣的運算 用`i`改寫,請同學試試看。請注意,這裡`x[2]` 的意義,是`x[1]` 的「下一個位置」:

x[i + 1] <- x[i] — 0.2 * x[i]

在完成上一個問題之後,我們只要讓`i=1` 的時候跑一次運算、 `i=2`再跑一次運算、一直重複這個動作到`i=99`。

在R 裡面,我們可 運用`for`這個語法來達成這個目的。

for(i in 1:99) x[i + 1] <- x[i] — 0.2 * x[i]

在R 中,`for(i in 1:99) expr`的語法代表的,就是讓`i`是為`1:99`的第一個element,也就是`i=1` 的狀況下去運算`expr`。

接著,讓`i`為`1:99`的第二個element,也就是`i=2`的狀況下去運算`expr`。

重複這個動作,直到`i`到`1:99`的最後一個element,也就是`i=99`的狀況。

提醒同學,這裡的expr就是`x[i + 1] <- x[i] — 0.2 * x[i]`。

其實大部分機器學習算法的核心,是非常類似我們上述所做的動作:

「不停的改善既有的答案」

接著,再讓我們回到第一個範例。在實務上,我們不可能無窮無盡的讓改善答案的過程一直做下去。

當答案「夠用」的時候,我們就想停止這個過程。

除了用預先規劃跑99 次的方式之外,另外一種常用的方式,是衡量每次改善的幅度,並且當改善幅度太小的時候,中斷執行的程序。

通常,我們會拿這次的答案和上一次的答案做比較,如果差異過小,就代表能改善的幅度已經太小,答案可能已經夠用了。

在這裡,我們利用`abs(x[2] -x[1])`可以計算第一次計算後答案的差異。這裡的`abs`是取絕對值的運算。

`abs`可以保證回傳的都是正數,在比大小時很常用。

同理,請同學計算第二次計算後答案的差異。

一個實務上常常採用的策略是:我們希望在答案差異小於0.01的時候,終止迴圈運算,否則就會一直計算。同時,我們希望迴圈最多跑99次。

要達到這樣的功能,我們需要先解決:「在改善幅度小於0.01的時候,終止迴圈運算,否則就會一直計算」的程式。 在R 之中,我們可以利用

`if (expr1)expr2`

這樣,R 只有在`expr1`為`TRUE`的時候,才會執行expr2。否則就會略過。

在R 的`break`函數則可以中斷迴圈。

x <- numeric(100)

x[1] <- 3

for (i in 1:99){

x[i+1] <- x[i]-0.2 [i]

if(abs(x[i+1]-x[i])<0.1) break }


建立自己的函數

R 中已經有非常多方便的工具,例如`mean`、`sd`等各種幫助使用者計算平均值、標準差的「函數」。而R 也可以讓使用者 建立自己的函數。

舉例來說,如果我們有一個矩陣`m` 和一個向量`y` ,我們想要找到一個向量`x`,讓`m %*% x`很靠近`y`。

這裡的`m` 可能是過去的一些資訊,而`y` 可能是我們感興趣的數字,我們想預測的數字,或是我們想要了解它是如何變化的數字。

舉例來說,`m` 是車速,而`y`則是煞車的滑行距離。 我們需要了解兩者的關係,才能建議合適的車速,以及與前車之間要保持的安全距離。

首先我們先看看m 的值。

我們再看看y 的值。

因為`m` 是一個50 * 2 的矩陣,所以在R 裡面找一個長度為2 的向量, 例如`c(-17, 4)`,我們就可以用`m %*% c(-17, 4)`來當作是對y 的趨近。

m %*% c(-17 ,4)

接著我們來評估`c(-17, 4)`是不是一個好的答案。

計算`m %*% c(-17, 4) — y`的平方的和(Sum of Square)

sum((m %*% c(-17, 4) — y)²)

通常在比較這個指標的時候,我們可以拿`mean(y) — y`的平方的和作為一個參考值。 這因為`y`的平均是一個趨近y 最最普通的一種方式,所以我們對`y` 的逼近應該要比`mean(y)`來的好。

計算以`mean(y) — y`的平方的和

sum((mean(y)-y)²)

在上面兩個操作的時候,同學重複的計算兩次和`y` 相差的平方的和。

這樣重複的計算, 在R中是可以透過「撰寫函數」來簡化的。

我們可以建立一個R 的函數物件`f` ,讓上述的兩個計算的程式碼從`sum((<???> — y)²)`變成`f(<???>)`的。


f <- function(x) sum((x — y)²)

當我們輸入:`f(mean(y))`時,R 就會進入到「函數的領域」(行話叫做「環境」,這部份在未來的RProgramming課程中會仔細解釋) `mean(y)`是`f`的第一個參數,也就是x ,所以R 在計算`sum((x — y)²)`時,會用`mean(y)` 的值 替換掉`x` ,所以我們才能拿到和`sum((mean(y) -y)²)`一樣的計算結果。

透過`f` ,我們就可以很方便的計算估計的誤差的平方和。

但是回到原本的問題,我們是想要找一個長度為2 的向量,讓m 和這個向量相乘後,可以很靠近y。

可是`f` 的參數`x` ,代表的意義已經是對`y`的估計了,這樣在使用上還是不夠方便。我們可以利用`f`來建立更方便的計算函數。

f1 <- function(x) f(m %*% x)

另外我們剛剛透過定義`f`和`f1`來簡化重複的動作。當我們在撰寫複雜的程式時,這類簡化的動作可以讓未來的工作效率。函數的設計,在近代軟體工程的領域中,也是有許多好的規範。

這份教材只是介紹R 的函數功能,如果未來同學有需要寫複雜的R程式,建議還是查查相關的資料後再動手,會讓自己的工作 效率更高。


在R 之中我們可以利用`optim` 來尋找一個函數的最小值。

我們先讓同學玩一玩`r` 之後,再解釋`optim` 。

這段玩的過程,也是當我們第一次使用R 強大的計算功能後,得到的結果。


另外一個R 很重要的特色是,函數本身也是一種物件。

R其實是「函數式語言」,而這是所有函數式語言的一個重要特色。所以,「函數可以當成參數給另外一個函數的」。

r <- optim(c(0,0),f1)

在`optim`的範例中,我們運用`f1`告訴R 我們在乎的指標(也就是透過和`m` 做相乘後與`y`的差別)
這裡的`f1`是一個動態的動作:R 每猜一個,例如`c(0,0)`的值,就會用`f1`來評估猜的好不好。

透過一些最佳化的演算法,R 就可以找到最好的猜測。

請同學輸入`r$par`看看`optim` 找到的最佳解

讓我們用迴歸分析的方法找到最好的解。請同學輸入`solve(t(m) %*% m, t(m) %*% y)`

找到的解是不是非常接近呢?

以上就是對於R 的函數功能的簡單簡介。

One clap, two clap, three clap, forty?

By clapping more or less, you can signal to us which stories really stand out.