This is an old revision of the document!
Table of Contents
Gradient Descent
task: y hat = a + b*x 의 regression 에서 a 와 b를 구하기
- regressin line으로 이루어지는 에측치로 결과되는 오차의 제곱의 합은 (sum of square residuals, ssr 혹은 sse) 최소값이 되어야 한다.
- 방법 1. (버릴 방법)
- 우선 b를 안다고 하고 정해두면 (가령 -2.4와 같이 – 비현실적이지만)
- 예측할 수 있는 a값의 range를 두고
- 하나씩 대입하여 그 때의 sum of square residual 을 (ssr 혹은 sse) 구해서 기록한다.
- 이때 우리는 sse값이 ()^2의 형태를 띄기 때문에 알파벳 U 형태를 띄는 것을 안다.
- 따라서 이 sse 그래프의 최소값이 될 때의 a값이 무엇인가를 구해보면 된다.
- 아래 r script는 이런 방법으로 구해 본것인데 아래처럼 두가지 단점이 있다.
- 우선 정확한 b값을 알 수는 없다.
- a 값을 장님 문고리 잡듯이 -40에서 40 사이의 점수를 0.1씩 증가시켜 보는 것이 너무 비효율적이다.
- 더불어 이렇게 구한 a값은 1/10 단위의 숫자를 구하게 되지 정확한 a값을 구하는 것은 아니다 (예를 들면 1.4로 a값을 구했는데 정확한 값은 1.38976 일 수도 있다.
rs01
library(ggplot2)
library(ggpmisc)
library(tidyverse)
library(data.table)
library(MASS)
# settle down
rm(list=ls())
rnorm2 <- function(n,mean,sd) {
mean + sd * scale(rnorm(n))
}
ss <- function(x) {
sum((x-mean(x))^2)
}
sp <- function(x, y) {
sum((x-mean(x))*(y-mean(y)))
}
# 1. mean of three variables
# iq, allowance, gpa
# x, x2, y
means <- c(108, 42, 3.2)
# allowance, iq, gpa
sds <- c(11, 8, 0.4) # standard deviations
# correlation matrix. note that correlation
# between v1 and v2 is near none (0.05)
corr_matrix <- matrix(c(1, 0.05, 0.4,
0.05, 1, 0.2,
0.4, 0.2, 1),
nrow = 3) # Target correlation matrix
# 2. covariance matrix
# diagonal matrix of SDs
sd_diag <- diag(sds)
sd_diag
# Calculate covariance matrix
cov_matrix <- sd_diag %*% corr_matrix %*% sd_diag
# 3. population data
# set.seed(32)
set.seed(432)
# n_pop <- 100000
# pop <- mvrnorm(n = n_pop, mu = means, Sigma = cov_matrix)
# pop <- as.data.frame(pop)
# colnames(pop) <- c("x", "x2", "y")
# cor(pop)
# n_sam <- 100
# sam <- pop |> slice_sample(n = n_sam, replace = T)
# head(sam)
# cor(sam)
n_sam <- 100
sam <- mvrnorm(n = n_sam, mu = means, Sigma = cov_matrix)
sam <- as.data.frame(sam)
colnames(sam) <- c("x", "x2", "y")
cor(sam)
y <- sam$y
x <- sam$x
x2 <- sam$x2
# check with regression
mo <- lm(y ~ x, data = sam)
summary(mo)
# graph
ggplot(data = sam, aes(x = x, y = y)) +
geom_point() +
stat_poly_line() +
stat_poly_eq(use_label(c("eq", "R2"))) +
theme_classic()
a <- summary(mo)$coefficient[1]
b <- summary(mo)$coefficient[2]
cat(a, b)
# We know what b and a are from the proof
bc <- sp(y, x)/ss(x)
ac <- mean(y) - bc * mean(x)
cat(ac, bc)
# but,
# finding a with b being fixed
# set.seed(191)
# Initialize random betas
# 우선 b를 고정하고 a만
# 변화시켜서 이해
b <- summary(mo)$coefficients[2] # 원래 b값
a <- 0
b.init <- b
a.init <- a
b.init
a.init
# Predict function:
predict <- function(x, a, b){
return (a + b * x)
}
# we use sum of square of error which oftentimes become big
sseloss<- function(predictions, y) {
residuals <- (y - predictions)
return(sum(residuals^2))
}
sses <- c() # for sum of square residuals
as <- c() # for as (intercepts)
summary(x)
for (i in seq(from = -40, to = 40, by = 0.01)) {
pred <- predict(x, i, b)
sse <- sseloss(pred, y)
sses <- append(sses, sse)
as <- append(as, i)
}
length(sses)
length(as)
min(sses)
max(sses)
min.pos.sses <- which(sses == min(sses))
min.pos.sses
print(as[min.pos.sses])
summary(mo)
plot(as, sses, type='l', lwd=2)
abline(v=as[min.pos.sses])
text(x = as[min.pos.sses], y = median(sses), col='red',
labels = paste(" sse = ", round(min(sses),4),
"\n is minimum value
when a =", as[min.pos.sses]),
pos=4)
ro01
> library(ggplot2)
> library(ggpmisc)
> library(tidyverse)
> library(data.table)
> library(MASS)
>
> # settle down
> rm(list=ls())
>
> rnorm2 <- function(n,mean,sd) {
+ mean + sd * scale(rnorm(n))
+ }
> ss <- function(x) {
+ sum((x-mean(x))^2)
+ }
> sp <- function(x, y) {
+ sum((x-mean(x))*(y-mean(y)))
+ }
>
> # 1. mean of three variables
> # iq, allowance, gpa
> # x, x2, y
> means <- c(108, 42, 3.2)
>
> # allowance, iq, gpa
> sds <- c(11, 8, 0.4) # standard deviations
> # correlation matrix. note that correlation
> # between v1 and v2 is near none (0.05)
> corr_matrix <- matrix(c(1, 0.05, 0.4,
+ 0.05, 1, 0.2,
+ 0.4, 0.2, 1),
+ nrow = 3) # Target correlation matrix
> # 2. covariance matrix
> # diagonal matrix of SDs
> sd_diag <- diag(sds)
> sd_diag
[,1] [,2] [,3]
[1,] 11 0 0.0
[2,] 0 8 0.0
[3,] 0 0 0.4
>
> # Calculate covariance matrix
> cov_matrix <- sd_diag %*% corr_matrix %*% sd_diag
>
> # 3. population data
> # set.seed(32)
> set.seed(432)
> # n_pop <- 100000
> # pop <- mvrnorm(n = n_pop, mu = means, Sigma = cov_matrix)
> # pop <- as.data.frame(pop)
> # colnames(pop) <- c("x", "x2", "y")
> # cor(pop)
>
> # n_sam <- 100
> # sam <- pop |> slice_sample(n = n_sam, replace = T)
> # head(sam)
> # cor(sam)
>
> n_sam <- 100
> sam <- mvrnorm(n = n_sam, mu = means, Sigma = cov_matrix)
> sam <- as.data.frame(sam)
> colnames(sam) <- c("x", "x2", "y")
> cor(sam)
x x2 y
x 1.0000000 0.1880415 0.4143930
x2 0.1880415 1.0000000 0.3373768
y 0.4143930 0.3373768 1.0000000
>
>
> y <- sam$y
> x <- sam$x
> x2 <- sam$x2
>
> # check with regression
> mo <- lm(y ~ x, data = sam)
> summary(mo)
Call:
lm(formula = y ~ x, data = sam)
Residuals:
Min 1Q Median 3Q Max
-0.92770 -0.20715 0.03368 0.20038 0.90512
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 1.614428 0.344622 4.685 9.04e-06 ***
x 0.014476 0.003212 4.508 1.82e-05 ***
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Residual standard error: 0.3346 on 98 degrees of freedom
Multiple R-squared: 0.1717, Adjusted R-squared: 0.1633
F-statistic: 20.32 on 1 and 98 DF, p-value: 1.818e-05
>
> # graph
> ggplot(data = sam, aes(x = x, y = y)) +
+ geom_point() +
+ stat_poly_line() +
+ stat_poly_eq(use_label(c("eq", "R2"))) +
+ theme_classic()
>
> a <- summary(mo)$coefficient[1]
> b <- summary(mo)$coefficient[2]
> cat(a, b)
1.614428 0.01447618> # We know what b and a are from the proof
> bc <- sp(y, x)/ss(x)
> ac <- mean(y) - bc * mean(x)
> cat(ac, bc)
1.614428 0.01447618>
> # but,
> # finding a with b being fixed
> # set.seed(191)
> # Initialize random betas
> # 우선 b를 고정하고 a만
> # 변화시켜서 이해
> b <- summary(mo)$coefficients[2] # 원래 b값
> a <- 0
>
> b.init <- b
> a.init <- a
> b.init
[1] 0.01447618
> a.init
[1] 0
>
> # Predict function:
> predict <- function(x, a, b){
+ return (a + b * x)
+ }
>
> # we use sum of square of error which oftentimes become big
> sseloss<- function(predictions, y) {
+ residuals <- (y - predictions)
+ return(sum(residuals^2))
+ }
>
> sses <- c() # for sum of square residuals
> as <- c() # for as (intercepts)
> summary(x)
Min. 1st Qu. Median Mean 3rd Qu. Max.
87.12 98.44 106.27 106.80 114.62 133.33
> for (i in seq(from = -40, to = 40, by = 0.01)) {
+ pred <- predict(x, i, b)
+ sse <- sseloss(pred, y)
+ sses <- append(sses, sse)
+ as <- append(as, i)
+ }
> length(sses)
[1] 8001
> length(as)
[1] 8001
>
> min(sses)
[1] 10.97429
> max(sses)
[1] 173187
> min.pos.sses <- which(sses == min(sses))
> min.pos.sses
[1] 4162
> print(as[min.pos.sses])
[1] 1.61
> summary(mo)
Call:
lm(formula = y ~ x, data = sam)
Residuals:
Min 1Q Median 3Q Max
-0.92770 -0.20715 0.03368 0.20038 0.90512
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 1.614428 0.344622 4.685 9.04e-06 ***
x 0.014476 0.003212 4.508 1.82e-05 ***
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Residual standard error: 0.3346 on 98 degrees of freedom
Multiple R-squared: 0.1717, Adjusted R-squared: 0.1633
F-statistic: 20.32 on 1 and 98 DF, p-value: 1.818e-05
> plot(as, sses, type='l', lwd=2)
> abline(v=as[min.pos.sses])
> text(x = as[min.pos.sses], y = median(sses), col='red',
+ labels = paste(" sse = ", round(min(sses),4),
+ "\n is minimum value
+ when a =", as[min.pos.sses]),
+ pos=4)
>


위의 두번째 그래프는 a값이 -40 에서 40 까지 0.1 단위로 변할 때의 sse값을 구한 후에 이를 기록하여 그래프로 만든 것이다. 이렇게 구한 sse 값들 중 최소값일 때의 a값을 regression의 a값으로 추정한 것이다. 이렇게 해서 구한 값은 summary(mo)에서의 a와 값과 같다.
SSE 대신에 MSE를 쓰기
위에서 sse값을 저장해서 그 중에 최소값이 13.98253 임을 알게 된것이다. 그러나 이 계산은 각각의 sse값이 아주 크게 되는 경우가 많다.
> min(sses)
[1] 13.98253
> max(sses)
[1] 180081.3
따라서 sse 대신에 mse 값을 구해서 저장한다. mse = sse 를 n으로 나눈 값 (mean square error (residual)).
rs.01
#####
# with mean square error (mse) instead of sse
b <- summary(mo)$coefficients[2]
a <- 0
# we use sum of square of error which oftentimes become big
mseloss <- function(predictions, y) {
residuals <- (y - predictions)
return(mean(residuals^2))
}
mses <- c() # for sum of square residuals
as <- c() # for as (intercepts)
for (i in seq(from = -40, to = 40, by = 0.01)) {
pred <- predict(x, i, b)
mse <- mseloss(pred, y)
mses <- append(mses, mse)
as <- append(as, i)
}
length(mses)
length(as)
min(mses)
max(mses)
min.pos.mses <- which(mses == min(mses))
min.pos.mses
print(as[min.pos.mses])
summary(mo)
plot(as, mses, type='l', lwd=2)
abline(v=as[min.pos.mses])
text(x = as[min.pos.mses], y = median(mses), col='red',
labels = paste(" mse = ", round(min(mses),4),
"\n is minimum value
when a =", as[min.pos.mses]),
pos=4)
ro.02
> #####
> # with mean square error (mse) instead of sse
>
> b <- summary(mo)$coefficients[2]
> a <- 0
>
> # we use sum of square of error which oftentimes become big
> mseloss <- function(predictions, y) {
+ residuals <- (y - predictions)
+ return(mean(residuals^2))
+ }
>
> mses <- c() # for sum of square residuals
> as <- c() # for as (intercepts)
>
> for (i in seq(from = -40, to = 40, by = 0.01)) {
+ pred <- predict(x, i, b)
+ mse <- mseloss(pred, y)
+ mses <- append(mses, mse)
+ as <- append(as, i)
+ }
> length(mses)
[1] 8001
> length(as)
[1] 8001
>
> min(mses)
[1] 0.1097429
> max(mses)
[1] 1731.87
> min.pos.mses <- which(mses == min(mses))
> min.pos.mses
[1] 4162
> print(as[min.pos.mses])
[1] 1.61
> summary(mo)
Call:
lm(formula = y ~ x, data = sam)
Residuals:
Min 1Q Median 3Q Max
-0.92770 -0.20715 0.03368 0.20038 0.90512
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 1.614428 0.344622 4.685 9.04e-06 ***
x 0.014476 0.003212 4.508 1.82e-05 ***
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Residual standard error: 0.3346 on 98 degrees of freedom
Multiple R-squared: 0.1717, Adjusted R-squared: 0.1633
F-statistic: 20.32 on 1 and 98 DF, p-value: 1.818e-05
> plot(as, mses, type='l', lwd=2)
> abline(v=as[min.pos.mses])
> text(x = as[min.pos.mses], y = median(mses), col='red',
+ labels = paste(" mse = ", round(min(mses),4),
+ "\n is minimum value
+ when a =", as[min.pos.mses]),
+ pos=4)
>
>
b값 구하기
이제는 a값을 고정하고 b값도 같은 방식으로 구해볼 수 있다
rs01
##############################################
# b값도 범위를 추측한 후에 0.01씩 증가시키면서
# 각 b값에서 mse값을 구해본후 가장 작은 값을
# 가질 때의 b값을 구하면 된다.
# 그러나 b값의 적절한 구간을 예측하는 것이
# 불가능하다 (그냥 추측뿐)
# 위의 y 데이터에서 y = 314*x + rnorm(. . .)
# 이라면 -30-30 구간은 적절하지 않은 구간이 된다.
# 더우기 a값을 정확히 알아야 b값을 추출할 수 있다.
# 이는 적절한 방법이 아니다.
b <- 1
a <- summary(mo)$coefficients[1]
b.init <- b
a.init <- a
# Predict function:
predict <- function(x, a, b){
return (a + b * x)
}
# And loss function is:
residuals <- function(predictions, y) {
return(y - predictions)
}
# we use sum of square of error which oftentimes become big
mseloss <- function(predictions, y) {
residuals <- (y - predictions)
return(mean(residuals^2))
}
mses <- c()
bs <- c()
for (i in seq(from = -40, to = 40, by = 0.01)) {
pred <- predict(x, b, i)
res <- residuals(pred, y)
mse <- mseloss(pred, y)
mses <- append(mses, mse)
bs <- append(bs,i)
}
min(mses)
max(mses)
min.pos.mses <- which(mses == min(mses))
print(bs[min.pos.mses])
summary(mo)
plot(bs, mses, type='l', lwd=2)
abline(v=bs[min.pos.mses])
text(x = bs[min.pos.mses], y = median(mses), col='red',
labels = paste(" mse = ", min(mses),
"\n is minimum value when b =",
round(bs[min.pos.mses],4)),
pos=4)
ro01
> ##############################################
> # b값도 범위를 추측한 후에 0.01씩 증가시키면서
> # 각 b값에서 mse값을 구해본후 가장 작은 값을
> # 가질 때의 b값을 구하면 된다.
> # 그러나 b값의 적절한 구간을 예측하는 것이
> # 불가능하다 (그냥 추측뿐)
> # 위의 y 데이터에서 y = 314*x + rnorm(. . .)
> # 이라면 -30-30 구간은 적절하지 않은 구간이 된다.
> # 더우기 a값을 정확히 알아야 b값을 추출할 수 있다.
> # 이는 적절한 방법이 아니다.
>
> b <- 1
> a <- summary(mo)$coefficients[1]
>
> b.init <- b
> a.init <- a
>
> # Predict function:
> predict <- function(x, a, b){
+ return (a + b * x)
+ }
>
> # And loss function is:
> residuals <- function(predictions, y) {
+ return(y - predictions)
+ }
>
> # we use sum of square of error which oftentimes become big
> mseloss <- function(predictions, y) {
+ residuals <- (y - predictions)
+ return(mean(residuals^2))
+ }
>
> mses <- c()
> bs <- c()
>
> for (i in seq(from = -40, to = 40, by = 0.01)) {
+ pred <- predict(x, b, i)
+ res <- residuals(pred, y)
+ mse <- mseloss(pred, y)
+ mses <- append(mses, mse)
+ bs <- append(bs,i)
+ }
>
> min(mses)
[1] 0.1627399
> max(mses)
[1] 18573746
> min.pos.mses <- which(mses == min(mses))
> print(bs[min.pos.mses])
[1] 0.02
> summary(mo)
Call:
lm(formula = y ~ x, data = sam)
Residuals:
Min 1Q Median 3Q Max
-1.05708 -0.25025 -0.00149 0.17215 0.85818
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 2.434345 0.426666 5.706 1.23e-07 ***
x 0.007499 0.003962 1.893 0.0613 .
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Residual standard error: 0.3777 on 98 degrees of freedom
Multiple R-squared: 0.03527, Adjusted R-squared: 0.02542
F-statistic: 3.582 on 1 and 98 DF, p-value: 0.06135
> plot(bs, mses, type='l', lwd=2)
> abline(v=bs[min.pos.mses])
> text(x = bs[min.pos.mses], y = median(mses), col='red',
+ labels = paste(" mse = ", min(mses),
+ "\n is minimum value when b =",
+ round(bs[min.pos.mses],4)),
+ pos=4)
>
a와 b를 동시에 구할 수 있는 방법은 없을까? 위의 방법으로는 어렵다. 일반적으로 우리는 a와 b값이 무엇이되는가를 미분을 이용해서 구할 수 있었다. R에서 미분의 해를 구하기 보다는 해에 접근하도록 하는 프로그래밍을 써서 a와 b의 근사값을 구한다. 이것을 gradient descent라고 부른다.
Gradient descend
위에서 a값이 무엇일 때 mse값이 최소가 될까를 봐서 이 때의 a값을 실제 $y = a + bx$ 의 a 값으로 삼았다. 이 때 a값의 추정을 위해서
seq(-10, 10, 0.01) 의 범위와 증가값을 가지고 일일이 대입하여 mse를 구아였다.
위에서의 0.01씩 증가시켜 대입하는 것이 아니라 처음 한 숫자에서 시작한 후 그 다음 숫자를 정한 후에 점진적으로 그 숫자 간격을 줄여가면서 보면 정율적으로 0.01씩 증가시키는 것 보다 효율적일 것 같다. 이 증가분을 구하기 위해서 미분을 사용한다.
점차하강 = 조금씩 깍아서 원하는 기울기 (미분값) 찾기
prerequisite:
표준편차 추론에서 평균을 사용하는 이유: 실험적_수학적_이해
deriviation of a and b in a simple regression
위의 문서는 a, b에 대한 값을 미분법을 이용해서 직접 구하였다. 컴퓨터로는 이렇게 하기가 쉽지 않다. 그렇다면 이 값을 반복계산을 이용해서 추출하는 방법은 없을까? gradient descent
\begin{eqnarray*}
\text{for a (constant)} \\
\\
\text{SSE} & = & \text{Sum of Square Residuals} \\
\text{Residual} & = & (Y_i - (a + bX_i)) \\
\\
\frac{\text{dSSE}}{\text{da}}
& = & \frac{\text{dResidual^2}}{\text{dResidual}} * \frac{\text{dResidual}}{\text{da}} \\
& = & 2 * \text{Residual} * \dfrac{\text{d}}{\text{da}} (Y_i - (a+bX_i)) \\
& \because & \dfrac{\text{d}}{\text{da}} (Y_i - (a+bX_i)) = -1 \\
& = & 2 * \sum{(Y_i - (a + bX_i))} * -1 \\
& = & -2 *\sum{\text{residual}} \\
& .. & -2 \frac{\sum{\text{residual}}}{n} \\
& = & -2 * \overline{\text{residual}} \\
\end{eqnarray*}
위 미분결과는 주어진 Xi값에서 얻는 a값을 의미한다.
아래 R code에서 gradient function을 참조.
\begin{eqnarray*} \text{for b, (coefficient)} \\ \\ \dfrac{\text{d}}{\text{db}} \sum{(Y_i - (a + bX_i))^2} & = & \sum{\dfrac{\text{dResidual}^2}{\text{db}}} \\ & = & \sum{\dfrac{\text{dResidual}^2}{\text{dResidual}}*\dfrac{\text{dResidual}}{\text{db}} } \\ & = & \sum{2*\text{Residual} * \dfrac{\text{dResidual}}{\text{db}} } \\ & = & \sum{2*\text{Residual} * (-X_i) } \;\;\;\; \\ & \because & \dfrac{\text{dResidual}}{\text{db}} = (Y_i - (a+bX_i)) = -X_i \\ & = & -2 X_i \sum{(Y_i - (a + bX_i))} \\ & = & -2 * X_i * \sum{\text{residual}} \\ & .. & -2 * X_i * \frac{\sum{\text{residual}}}{n} \\ & = & -2 * \overline{X_i * \text{residual}} \\ \end{eqnarray*}
위의 미분결과는 주어진 Xi 값에서의 b값을 즉 기울기 값을 구하것을 의미한다.
위의 설명은 Sum of Square값을 미분하는 것을 전제로 하였지만, Mean Square 값을 (Sum of Square값을 N으로 나눈 것) 대용해서 이해할 수도 있다. 아래의 code는 (미분을 이해한다는 것을 전제로) b값과 a값이 변할 때 msr (mean square residual) 값이 어떻게 변하는가를 알려주는 것이다.
gradient <- function(x, y, predictions){
error = y - predictions
db = -2 * mean(x * error)
da = -2 * mean(error)
return(list("b" = db, "a" = da))
}
위 펑션으로 얻은 da와 db값을 초기에 설정한 a, b 값에 더해 준 값을 다시 a, b값으로 하여 gradient 펑션을 통해서 다시 db, da값을 구하고 이를 다시 이전 단계에서 구한 a, b값에 더하여 그 값을 다시 a, b값을 하여 . . . .
위를 반복한다. 단, db값과 da값을 그냥 대입하기 보다는 초기에 설정한 learning.rate값을 (0.01 예를 들면) 곱하여 구한 값을 더하게 된다. 이것이 아래의 code이다.
a 와 b 값을 gradient descent 방법을 이용하여 한꺼번에 구하기
rs01
# 아래는 a 와 b 를 랜덤하게 정한 후 (rnorm)
# 두 가지 일을 하게 된다. 첫 째는
# 이 때의 mse값을 저장한다. 둘째는
# a, b 값을 가질 때의 a 와 b에 대한 미분 값을
# 구한 후, 이 값에 1보다 작은 값을 곱하여 (learning rate)
# 구한 값을 다음의 a 와 b 값으로 써서 다시
# 이를 대입하여 mse값을 구하고 이 때의 미분 값을
# 구한다. 이 미분하여 구한 a, b값을 저장한 후에
# 다시 learning rate값을 곱하여 이를 다음의 a, b
# 값으로 사용한다. . . . 이를 반복한다.
a <- rnorm(1)
b <- rnorm(1)
a.init <- a
b.init <- b
# 이 때의 db, da는 무슨 값인가?
# 미분결과값이므로 db는 기울기값
# da는 그 당시의 a값
gradient <- function(x, y, predictions){
error = y - predictions
db = -2 * mean(x * error)
da = -2 * mean(error)
return(list("b" = db, "a" = da))
# return(c(db, da))
}
mseloss <- function(predictions, y) {
residuals <- (y - predictions)
return(mean(residuals^2))
}
# Train the model with scaled features
learning.rate = 1e-1
# Record Loss for each epoch:
as = c()
bs = c()
mses = c()
zx <- (x-mean(x))/sd(x)
nlen <- 50
for (epoch in 1:nlen) {
predictions <- predict(zx, a, b)
residual <- residuals(predictions, y)
loss <- mseloss(predictions, y)
mses <- append(mses, loss)
grad <- gradient(zx, y, predictions)
step.b <- grad$b * learning.rate
step.a <- grad$a * learning.rate
b <- b-step.b
a <- a-step.a
as <- append(as, a)
bs <- append(bs, b)
}
mses
as
bs
# scaled
a
b
# unscale coefficients to make them comprehensible
# see http://commres.net/wiki/gradient_descent#why_normalize_scale_or_make_z-score_xi
# and
# http://commres.net/wiki/gradient_descent#how_to_unnormalize_unscale_a_and_b
#
a = a - (mean(x) / sd(x)) * b
b = b / sd(x)
a
b
# changes of estimators
as <- as - (mean(x) /sd(x)) * bs
bs <- bs / sd(x)
as
bs
parameters <- data.frame(as, bs, mses)
cat(paste0("Intercept: ", a, "\n", "Slope: ", b, "\n"))
summary(lm(y~x, data=sam))$coefficients
if (as[nlen] > as[1]) {
amax <- as[nlen]
amin <- as[1]
} else {
amax <- as[1]
amin <- as[nlen]
}
if (max(y) > amax) {
y.max <- max(y)
} else {
y.max <- amax
}
if (min(y) > amin) {
y.min <- amin
} else {
y.min <- min(y)
}
max(y)
min(y)
y.max
y.min
ggplot(sam, aes(x = x, y = y)) +
geom_point(size = 2) +
coord_cartesian(xlim = c(0, max(x)+30), ylim = c(y.min-2,y.max+2)) +
geom_abline(aes(intercept = as, slope = bs),
data = parameters, linewidth = 0.5,
color = 'green') +
geom_abline(aes(intercept = as, slope = bs),
data = parameters %>% slice_head(),
linewidth = 1, color = 'blue') +
geom_abline(aes(intercept = as, slope = bs),
data = parameters %>% slice_tail(),
linewidth = 1, color = 'red') +
# stat_poly_line() +
stat_poly_eq(use_label(c("eq", "R2"))) +
theme_classic() +
labs(title = 'Gradient descent. blue: start, red: end, green: gradients')
summary(lm(y~x))
data.frame(head(as), head(bs))
data.frame(tail(as), tail(bs))
a
b
ggplot(sam, aes(x = x, y = y)) +
geom_point(size = 2) +
geom_abline(aes(intercept = as, slope = bs),
data = parameters %>% slice_tail(),
linewidth = 1, color = 'red') +
stat_poly_line() +
stat_poly_eq(use_label(c("eq", "R2"))) +
theme_classic() +
labs(title = 'Gradient descent. blue: start, red: end, green: gradients')
ro01
> # 아래는 a 와 b 를 랜덤하게 정한 후 (rnorm)
> # 두 가지 일을 하게 된다. 첫 째는
> # 이 때의 mse값을 저장한다. 둘째는
> # a, b 값을 가질 때의 a 와 b에 대한 미분 값을
> # 구한 후, 이 값에 1보다 작은 값을 곱하여 (learning rate)
> # 구한 값을 다음의 a 와 b 값으로 써서 다시
> # 이를 대입하여 mse값을 구하고 이 때의 미분 값을
> # 구한다. 이 미분하여 구한 a, b값을 저장한 후에
> # 다시 learning rate값을 곱하여 이를 다음의 a, b
> # 값으로 사용한다. . . . 이를 반복한다.
> a <- rnorm(1)
> b <- rnorm(1)
> a.init <- a
> b.init <- b
>
> # 이 때의 db, da는 무슨 값인가?
> # 미분결과값이므로 db는 기울기값
> # da는 그 당시의 a값
> gradient <- function(x, y, predictions){
+ error = y - predictions
+ db = -2 * mean(x * error)
+ da = -2 * mean(error)
+ return(list("b" = db, "a" = da))
+ # return(c(db, da))
+ }
>
> mseloss <- function(predictions, y) {
+ residuals <- (y - predictions)
+ return(mean(residuals^2))
+ }
>
> # Train the model with scaled features
> learning.rate = 1e-1
>
> # Record Loss for each epoch:
> as = c()
> bs = c()
> mses = c()
> zx <- (x-mean(x))/sd(x)
>
> nlen <- 50
> for (epoch in 1:nlen) {
+ predictions <- predict(zx, a, b)
+ residual <- residuals(predictions, y)
+ loss <- mseloss(predictions, y)
+ mses <- append(mses, loss)
+
+ grad <- gradient(zx, y, predictions)
+ step.b <- grad$b * learning.rate
+ step.a <- grad$a * learning.rate
+ b <- b-step.b
+ a <- a-step.a
+
+ as <- append(as, a)
+ bs <- append(bs, b)
+ }
> mses
[1] 13.5910678 8.7489402 5.6498620 3.6663771 2.3968985
[6] 1.5844012 1.0643830 0.7315586 0.5185427 0.3822072
[11] 0.2949490 0.2391016 0.2033579 0.1804810 0.1658392
[16] 0.1564681 0.1504703 0.1466316 0.1441747 0.1426022
[21] 0.1415958 0.1409517 0.1405394 0.1402755 0.1401067
[26] 0.1399986 0.1399294 0.1398851 0.1398568 0.1398386
[31] 0.1398270 0.1398196 0.1398149 0.1398118 0.1398099
[36] 0.1398086 0.1398078 0.1398073 0.1398070 0.1398068
[41] 0.1398066 0.1398066 0.1398065 0.1398065 0.1398064
[46] 0.1398064 0.1398064 0.1398064 0.1398064 0.1398064
> as
[1] 0.3157929 0.9003813 1.3680520 1.7421886 2.0414978 2.2809453
[7] 2.4725032 2.6257495 2.7483466 2.8464243 2.9248864 2.9876561
[13] 3.0378718 3.0780445 3.1101825 3.1358930 3.1564614 3.1729161
[19] 3.1860799 3.1966109 3.2050357 3.2117755 3.2171674 3.2214809
[25] 3.2249317 3.2276923 3.2299008 3.2316677 3.2330811 3.2342119
[31] 3.2351165 3.2358402 3.2364191 3.2368823 3.2372528 3.2375492
[37] 3.2377863 3.2379761 3.2381278 3.2382492 3.2383464 3.2384241
[43] 3.2384862 3.2385360 3.2385758 3.2386076 3.2386330 3.2386534
[49] 3.2386697 3.2386827
> bs
[1] 0.32915377 0.27820741 0.23734843 0.20457952 0.17829886
[6] 0.15722177 0.14031795 0.12676108 0.11588847 0.10716864
[11] 0.10017533 0.09456670 0.09006858 0.08646109 0.08356788
[16] 0.08124752 0.07938660 0.07789414 0.07669718 0.07573723
[21] 0.07496734 0.07434989 0.07385470 0.07345756 0.07313905
[26] 0.07288360 0.07267873 0.07251443 0.07238266 0.07227698
[31] 0.07219222 0.07212425 0.07206973 0.07202601 0.07199095
[36] 0.07196282 0.07194027 0.07192218 0.07190768 0.07189604
[41] 0.07188671 0.07187923 0.07187323 0.07186841 0.07186455
[46] 0.07186146 0.07185897 0.07185698 0.07185539 0.07185410
>
> # scaled
> a
[1] 3.238683
> b
[1] 0.0718541
>
> # unscale coefficients to make them comprehensible
> # see http://commres.net/wiki/gradient_descent#why_normalize_scale_or_make_z-score_xi
> # and
> # http://commres.net/wiki/gradient_descent#how_to_unnormalize_unscale_a_and_b
> #
> a = a - (mean(x) / sd(x)) * b
> b = b / sd(x)
> a
[1] 2.434234
> b
[1] 0.00749967
>
> # changes of estimators
> as <- as - (mean(x) /sd(x)) * bs
> bs <- bs / sd(x)
>
> as
[1] -3.36927396 -2.21431158 -1.28920093 -0.54819753 0.04533893
[6] 0.52075655 0.90156258 1.20658590 1.45090812 1.64660934
[11] 1.80336556 1.92892713 2.02950197 2.11006255 2.17459180
[16] 2.22627998 2.26768248 2.30084615 2.32741050 2.34868878
[21] 2.36573290 2.37938544 2.39032129 2.39908106 2.40609777
[26] 2.41171827 2.41622039 2.41982667 2.42271538 2.42502929
[31] 2.42688279 2.42836748 2.42955676 2.43050941 2.43127250
[36] 2.43188376 2.43237340 2.43276561 2.43307979 2.43333146
[41] 2.43353305 2.43369454 2.43382389 2.43392751 2.43401051
[46] 2.43407700 2.43413026 2.43417292 2.43420710 2.43423447
> bs
[1] 0.034354957 0.029037503 0.024772905 0.021352697 0.018609691
[6] 0.016409799 0.014645487 0.013230508 0.012095695 0.011185575
[11] 0.010455658 0.009870265 0.009400780 0.009024253 0.008722279
[16] 0.008480095 0.008285864 0.008130090 0.008005160 0.007904966
[21] 0.007824610 0.007760165 0.007708480 0.007667028 0.007633784
[26] 0.007607122 0.007585740 0.007568591 0.007554837 0.007543807
[31] 0.007534961 0.007527866 0.007522176 0.007517613 0.007513953
[36] 0.007511018 0.007508664 0.007506776 0.007505262 0.007504047
[41] 0.007503073 0.007502292 0.007501666 0.007501164 0.007500761
[46] 0.007500438 0.007500178 0.007499971 0.007499804 0.007499670
>
> parameters <- data.frame(as, bs, mses)
>
> cat(paste0("Intercept: ", a, "\n", "Slope: ", b, "\n"))
Intercept: 2.43423447454751
Slope: 0.00749967017274726
> summary(lm(y~x, data=sam))$coefficients
Estimate Std. Error t value Pr(>|t|)
(Intercept) 2.434344703 0.426665789 5.705507 1.233509e-07
x 0.007499129 0.003962082 1.892724 6.134554e-02
>
> if (as[nlen] > as[1]) {
+ amax <- as[nlen]
+ amin <- as[1]
+ } else {
+ amax <- as[1]
+ amin <- as[nlen]
+ }
> if (max(y) > amax) {
+ y.max <- max(y)
+ } else {
+ y.max <- amax
+ }
> if (min(y) > amin) {
+ y.min <- amin
+ } else {
+ y.min <- min(y)
+ }
> max(y)
[1] 4.090577
> min(y)
[1] 2.185787
> y.max
[1] 4.090577
> y.min
[1] -3.369274
>
> ggplot(sam, aes(x = x, y = y)) +
+ geom_point(size = 2) +
+ coord_cartesian(xlim = c(0, max(x)+30), ylim = c(y.min-2,y.max+2)) +
+ geom_abline(aes(intercept = as, slope = bs),
+ data = parameters, linewidth = 0.5,
+ color = 'green') +
+ geom_abline(aes(intercept = as, slope = bs),
+ data = parameters %>% slice_head(),
+ linewidth = 1, color = 'blue') +
+ geom_abline(aes(intercept = as, slope = bs),
+ data = parameters %>% slice_tail(),
+ linewidth = 1, color = 'red') +
+ # stat_poly_line() +
+ stat_poly_eq(use_label(c("eq", "R2"))) +
+ theme_classic() +
+ labs(title = 'Gradient descent. blue: start, red: end, green: gradients')
>
> summary(lm(y~x))
Call:
lm(formula = y ~ x)
Residuals:
Min 1Q Median 3Q Max
-1.05708 -0.25025 -0.00149 0.17215 0.85818
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 2.434345 0.426666 5.706 1.23e-07 ***
x 0.007499 0.003962 1.893 0.0613 .
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Residual standard error: 0.3777 on 98 degrees of freedom
Multiple R-squared: 0.03527, Adjusted R-squared: 0.02542
F-statistic: 3.582 on 1 and 98 DF, p-value: 0.06135
>
> data.frame(head(as), head(bs))
head.as. head.bs.
1 -3.36927396 0.03435496
2 -2.21431158 0.02903750
3 -1.28920093 0.02477290
4 -0.54819753 0.02135270
5 0.04533893 0.01860969
6 0.52075655 0.01640980
> data.frame(tail(as), tail(bs))
tail.as. tail.bs.
1 2.434011 0.007500761
2 2.434077 0.007500438
3 2.434130 0.007500178
4 2.434173 0.007499971
5 2.434207 0.007499804
6 2.434234 0.007499670
> a
[1] 2.434234
> b
[1] 0.00749967
>
> ggplot(sam, aes(x = x, y = y)) +
+ geom_point(size = 2) +
+ geom_abline(aes(intercept = as, slope = bs),
+ data = parameters %>% slice_tail(),
+ linewidth = 1, color = 'red') +
+ stat_poly_line() +
+ stat_poly_eq(use_label(c("eq", "R2"))) +
+ theme_classic() +
+ labs(title = 'Gradient descent. blue: start, red: end, green: gradients')
>
Why normalize (scale or make z-score) xi
x 변인의 측정단위로 인해서 b 값이 결정되게 되는데 이 때의 b값은 상당하고 다양한 범위를 가질 수 있다. 가령 월 수입이 (인컴) X 라고 한다면 우리가 추정해야 (추적해야) 할 b값은 수백만이 될 수도 있다.이 값을 gradient로 추적하게 된다면 너무도 많은 iteration을 거쳐야 할 수 있다. 변인이 바뀌면 이 b의 추적범위도 드라마틱하게 바뀌게 된다. 이를 표준화한 x 점수를 사용하게 된다면 일정한 learning rate와 iteration만으로도 정확한 a와 b를 추적할 수 있게 된다.
How to unnormalize (unscale) a and b
\begin{eqnarray*} y & = & a + b * x \\ & & \text{we use z instead of x} \\ & & \text{and } \\ & & z = \frac{(x - \mu)}{\sigma} \\ & & \text{suppose that the result after calculation be } \\ y & = & k + m * z \\ & = & k + m * \frac{(x - \mu)}{\sigma} \\ & = & k + \frac{m * x}{\sigma} - \frac{m * \mu}{\sigma} \\ & = & k - \frac{m * \mu}{\sigma} + \frac{m * x}{\sigma} \\ & = & \underbrace{k - \frac{\mu}{\sigma} * m}_\text{ 1 } + \underbrace{\frac{m}{\sigma}}_\text{ 2 } * x \\ & & \text{therefore, a and be that we try to get are } \\ a & = & k - \frac{\mu}{\sigma} * m \\ b & = & \frac{m}{\sigma} \\ \end{eqnarray*}





