Date-Time Conversion Function in R


R에서는 이것의 처리를 위해서 POSIXlt POSICct를 사용 할 수 있다.

예제

> Sys.time()
[1] "2015-12-12 13:08:11 KST"
w
> Sys.timezone()
[1] "Asia/Seoul"

> Sys.Date()
[1] "2015-12-12"

현재 datetime을 class로 받은다음 그것을 integer로 변경하는 방법

> z <- Sys.time()
> z
[1] "2015-12-12 13:27:17 KST"
> unclass(z)
[1] 1449894437

#다시 원래대로 복구함
> as.POSIXct(1449894437,origin="1970-01-01")
[1] "2015-12-12 13:27:17 KST"

서로 다른 시간으로 변경하

> as.POSIXlt(Sys.time(), "GMT")
[1] "2015-12-12 04:45:42 GMT"
> as.POSIXlt(Sys.time(), "EST5EDT")  # the current time in New York
[1] "2015-12-11 23:46:48 EST"
> as.POSIXlt(Sys.time(), "EST" )     # ditto, ignoring DST
[1] "2015-12-11 23:46:48 EST"
> as.POSIXlt(Sys.time(), "HST")      # the current time in Hawaii
[1] "2015-12-11 18:46:48 HST"
> as.POSIXlt(Sys.time(), "Australia/Darwin")
[1] "2015-12-12 14:16:48 ACST"

timestamp (milliseconds) 처리하기

#POSIXct 함수를 이용한
time <- 1433848856453
timezone <- 10800000
options(digits.secs=3)
as.POSIXct((time+timezone)/1000, origin="1970-01-01")

lt와 ct의 차이

lt는 list time의 약어이고 ct는 continuous time`의 약어이다.

  • as.POSIXlt(): 시계열분석이나 년/월/일/시간/분/초 단위로 구분해서 연산을 해야 하는 경우 해당 함수로 날짜/시간 type으로 바꾸어주면 편하다.

  • as.POSIXct():

참고자료

http://astrostatistics.psu.edu/su07/R/html/base/html/as.POSIXlt.html
http://stackoverflow.com/questions/1962278/dealing-with-timestamps-in-r
지수표기를 숫자표기로 변경하는 R 옵션


Handling json in R using jsonlite


JSON format을 R에서 다루기 위해서는 두개의 package가 조재한다.
첫 번째는 rJSON package이고 두 번째는 jsonlite package이다.
여기서는 jsonlite를 다룬다. 이유는 rJSON을 fork하여 확장한것이 jsonlite이기 때문이다. version도 2015년 11월로 Post를 작성 하고 있는 지금도 계속해서 개선되어지고 있는 package이기 때문이다.

지원하는 함수들의 목록은 아래와 같다.

  • flatten: Flatten nested data frames
  • fromJSON: Convert R objects to/from JSON
  • prettify: Prettify or minify a JSON string
  • rbind.pages: Combine pages into a single data frame
  • serializeJSON: serialize R objects to JSON
  • stream_in: Streaming JSON input/output
  • unbox: Unbox a vector or data frame
  • validate: Validate JSON

Streaming JSON input/output

When can nested dataframe be appeared ?

아래는 3개의 data frame이 nested된 구조를 JSON으로 병경한 것이다.

[
{
"driver":"Bowser",
"occupation":"Koopa",
"vehicle":
    {
    "model":"Piranha Prowler","stats":
        {
            "speed":55,"weight":67,"drift":35
        }
    }
},
{
"driver":"Peach","occupation":"Princess","vehicle":
    {
    "model":"Royal Racer","stats":
        {
            "speed":34,"weight":24,"drift":32
        }
    }
}
] 

아래와 같이 ""안에 들어 있으면 그냥 value로 처리된다.

"data":"{}"

참고자료

http://rpackages.ianhowson.com/cran/jsonlite/man/stream_in.html
JSON 공식 사이트


'AI > R Basic' 카테고리의 다른 글

Do.call()  (0) 2016.02.05
날짜 시간 변환 (Date-Time Conversion Function in R)  (0) 2015.12.14
통계처리 (기본)  (0) 2015.10.28
데이터 분리 및 병합 (split, subset, merge)  (1) 2015.10.22
doBy package, 데이터 그룹 처리  (0) 2015.10.22

Ensemble method: Bagging (bootstrap aggregating)


일반적인 ensemble 방법의 하나가 bootstrap aggregating인 Bagging 방법이다.

Leo Breiman에 의해서 1994년에 만들어 졌다.


bagging은 오리지널 트레이닝 셋으로 부터 bootstrap 샘플링 방법을 이용해서 여러개의 트레이닝 데이터를 생성하는 방법이다.

이렇게 생성된 데이터 셋은 하나의 모델을 생성하기 위해서 사용 된다.


상대적으로 Ensemble 기법들 중에서는 Bagging은 단순한 방법에 속하지만 꽤 잘 동작 한다.

특히 Decision Tree 알고리즘의 경우 약간의 입력 데이터의 변화에도 급격히 다른 결과값을 이끌어 내므로

Bagging으로 이것을 보완 할 수 있다.



ipred package는 bagged decision tree를 구현한 것이다.


'AI > Machine Learning with R' 카테고리의 다른 글

Caret의 이해  (0) 2016.03.06
Cross Validation  (0) 2016.02.26
Bootstrapping  (0) 2015.11.19
Principal Component Analysis (PCA)  (0) 2015.11.18
Evaluating Model Performance with R  (2) 2015.11.12

Bootstrapping


resubstitution error는 한글로 재치환오류라고 하며 트레이닝 셋에서 훈련된 모델에서의 에러를 말한다.

다른 용어로는 In sample error라고도 한다.

당연히 수치는 긍정적으로 평가된다. 왜냐하면 같은 데이터로 훈련하고 테스트 하기 때문이다.



Bootstrap sampling

큰 데이터 셋을 추정하기 위해서 임의로 여러번 샘플링을 하는것을 말한다.

When this principle is applied to machine learning model performance, it implies the creation of several randomly-selected training and test datasets, which are then used to estimate performance statistics.

The results from the various random datasets are then averaged to obtain a final estimate of future performance.


어떤 점이 그럼 k-fold Cross Validation이랑 다른 것일까?

이 방법은 생성한 각각의 구획들이 단 한번만 나타나게 된다.


하지만 bootstrap의 방법은 복원 추출을 하기 때문에 여러번 나올 수 있다.

증명에 의하면 초기 데이터 셋의 수인 N이 엄청 크다면, bootstrap으로는 63.2%를 커버한다는 증명이 있다.


결국 36.8%는 트레이닝 셋이다.


original data의 size가 N 레코드라고 하자. 이때 bootstrap으로 샘플링을 수행하면 N번 수행하면 당연히 N개의 레코드가 있고

이러한 N개의 레코드는 원래 original data size인 N개 중 63.2%를 포함한다고 한다.


N번의 bootstrap sampling에서 단일 record가 선택 되어질 확률은 아래와 같다.

$$1-(1-\frac{1}{N})^{N}$$

이것은 asymptotically approaches $1-e^{-1}=0.632$에 근접한다.

이렇게 bootstrap sampling으로 포함되지 않은 것들은 test set으로 자동으로 포함된다.


이에반에 10-fold cv를 할경우 90%가 결국 트레이닝이 된다.


N이 충분히 크다면 bootstrap은 더 낮은 representative를 보인다.


당연히 63.2%의 트레이닝 셋을 가지고 테스트 하기 때문에 더 큰 트레이닝셋을 가지고 만든 모델보다 더 낮은 정확도를 가지게 된다.


최종적인 error rate의 계산식은 아래와 같다.

$$ error = 0.632 \times error_{test} + 0.368 \times error_{train}$$


cross validation 보다 더 좋은 bootstap의 장점은 더 작은 dataset에서 잘 동작하는 특성이 있다는 것이다.

또한 bootstrap sampling은 모델 정확도 향상의 효과가 있을 수도 있다




주어진 데이터로 부터 복원 표본을 구하는 작업을 여러 번 반복해 원하는 값을 추정 하는 것이다.


예를 들어 보면,

평균의 신뢰 구간을 구하는 경우를 생각해보자.


평균의 신뢰 구간은 평균이 속할 범위를 95% 신뢰도로 찾는 것이다.

부트스트래핑은 데이터로 부터 표본을 복원 추출로 구하고 이들의 평균을 기록하는 일을 반복한다.


이렇게 구한 평균들을 나열한 뒤 가장 작은 값으로 부터 2.5% 가장 큰 값으로 부터 97.5% 지점의 평균 두 개를 구한다.

그러면 두 평균 사이의 구간이 평균 95% 신뢰 구간이 된다.


실제 코드로 구현해 보자.

평균 30, 표준편차 3인 정규 분포로부터 1,000개의 난수를 만들고 이 난수로부터 역으로 평균을 추정해보자.

x <- rnorm(1000, mean=30, sd=3)
t.test(x)
	One Sample t-test
data:  x
t = 308, df = 999, p-value < 2.2e-16
alternative hypothesis: true mean is not equal to 0
95 percent confidence interval:
 29.72329 30.10447
sample estimates:
mean of x 
 29.91388 

t검정 결과 평균의 신뢰 구간이 (29.78891, 30.16317)로 구해졌다. 

library(foreach)
bootmean <- foreach(i=1:10000, .combine=c) %do% {
    return(mean(x[sample(1:NROW(x), replace=TRUE)]))
}

이제는 부트스트래핑으로 복원 추출을 통해 샘플을 구하고 평균을 구하는 작업을 10,000회 반복해보자.

평균의 신뢰구간은 이 평균들의 하위 2.5%, 상위 97.5%에 속하는 값의 구간이다.

bootmean <- sort(bootmean)
bootmean[c(10000*0.025, 10000*0.975)]
[1] 29.72578 30.10531

t검정과 유사한 값이 부트스트랩핑으로도 구해진것을 알 수 있다.


부트스트래핑을 사용한 모델의 정확도 평가도 마찬가지로 수행된다.

데이터의 일부를 트레이닝 데이터로, 일부를 테스트 데이터로 복원 추출을 통해서 구한다.

그리고 이를 수회 반복하여 정확도를 추정한다. 이것이 trainControl()에서 method="boot"가 하는 일이다.



.







'AI > Machine Learning with R' 카테고리의 다른 글

Cross Validation  (0) 2016.02.26
Ensemble method: Bagging (bootstrap aggregating)  (0) 2015.11.19
Principal Component Analysis (PCA)  (0) 2015.11.18
Evaluating Model Performance with R  (2) 2015.11.12
Naive Bayes with Caret package (R)  (0) 2015.11.12

Principal Component Analysis (PCA)


주성분 분석은 데이터에 많은 변수가 있을 때 변수의 수를 줄이는 차원 감소(Dimensionality Reduction)기법 중 하나다.

PCA는 변수들을 주성분(Principal Component)이라 부르는 선형적인 상관관계가 없는 다른 변수들로 재표현한다.


무슨말인가 하면,


선형적인 관계가 있는 두 변수가 아래와 같이 있다고 가정해보자.

A = 2c+3

B= 3c

이다.

이때 Y = f(A,B)라는 어떤 모델이 있다고 하자.

이럴때 실제로 A,B 둘 사이에는 C라는 변수에 의한 선형적 관계가 있는데 둘 다 이용해서 Y를 예측하는 모델을 생성할 필요가 있을까?

이와 같을 때는 f(A,B)로 Y를 예측하기 보다는 그냥 f(C)로 Y를 예측하는것이 더 좋다.


Principal component들은 원 데이터의 분산(퍼짐 정도)을 최대한 보존하는 방법으로 구한다.

좀 더 자세한 내용은 아래의 이전 포스트를 참조하자.

practical machine learning (johns hopkins)

machine learning (standford)



R을 이용해서 실제로 적용해 보기 [R프로그래밍 실습서]


pincomp() 함수 또는 preComp()를 이용한다.


pincomp: 주성분 분석을 수행 한다.

x, # 행렬 또는 데이터 프레임

cor=FASLE # cor=FALSE면 공분산 행렬, TRUE면 상관 행렬을 사용한 주성분 분석을 한다.


아래와 같이 1:10을 저장한 x

약간의 noise를 추가한 y. 즉 x + noise = y 이다.

마지막으로 z는 x + y 에 noise를 추가한 것이다.

x <- 1:10

y <- x + runif(10, min=-.5, max=.5)

z <- x + y + runif(10, min=-10, max=.10)

data frame으로 내용을 출력해보면 아래와 같다.

> (data <- data.frame(x,y,z))

    x         y          z

1   1  1.413629 -0.5826325

2   2  2.021631 -0.9343342

3   3  3.045810 -3.1059315

4   4  3.857941  6.3310046

5   5  5.418124  0.4202864

6   6  6.306387  6.6968121

7   7  7.488154 13.1231456

8   8  8.148416 12.0075606

9   9  9.260750 10.7979732

10 10 10.086332 16.6841931


# do PCA using data

pr <- princomp(data)

summary(pr)


Importance of components:

                          Comp.1    Comp.2       Comp.3

Standard deviation     7.5667362 1.5335677 0.1356764036

Proportion of Variance 0.9602481 0.0394432 0.0003087272

Cumulative Proportion  0.9602481 0.9996913 1.0000000000


위 데이터들은 주성분들이 원 데이터의 분산 중 얼마만큼을 설명해 주는지를 알 수 있다.

Proportion of Variance 행을 보면 첫 번째 주성분은 데이터의 분산 중 92.86%를 설명해 주며,

두 번째 주성분은 데이터의 분산 중 7.1%를 설명함을 알 수 있다.

세 번째 주성분은 가장작은 0.03%의 분산을 설명 한다.

마지막 행의 Cumulative Proportion은 Proportion of Variance의 누적 값이다.


결국 원 데이터의 분산은 첫 번째와 두 번째 주성분에 의해 99.97%가 포함됨을 알 수 있다.

이들 두 주성분상의 좌표는 scores를 보고 구하면 된다.


결국 x,y,z의 데이터 분포는 그냥 2개의 차원으로 축소가 된것이다. 아래와 같은 좌표값을 가지게 된다.

> pr$scores[,1:2]

           Comp.1      Comp.2

 [1,]   8.9555327  1.91623469

 [2,]   8.6788410  0.76263371

 [3,]   9.8173286 -1.57583908

 [4,]   1.0457826  2.13444485

 [5,]   5.2072178 -2.44130849

 [6,]  -0.8729855 -0.38916205

 [7,]  -7.1881748  1.55801181

 [8,]  -6.8265993 -0.01728680

 [9,]  -6.5475880 -1.91981691

[10,] -12.2693552 -0.02791173



알츠하이머(Alzheimer)를 이용한 예제 [Practical Machine Learning HW-2]


데이터를 불러온다.

set.seed(3433)

library(AppliedPredictiveModeling)

data(AlzheimerDisease)

adData = data.frame(diagnosis, predictors)

inTrain = createDataPartition(adData$diagnosis, p = 3/4)[[1]]

training = adData[inTrain, ]

testing = adData[-inTrain, ]


위 데이터 셋에서 "IL"로 시작하는 트레이닝 셋 variables(변수)들에 대해서만 PCA를 수행한다.

preProcess()를 이용하며, PCA 수행결과 80%의 variance(분산)를 capture할 수 있는 변수들의 갯수를 결정해 보자.


먼저 grep과 정규표현으로 해당 열 데이터를 수집한다.

IL_str <- grep("^IL", colnames(training), value = TRUE)


아래와 같이 caret package의 preProcess()를 이용해서 pca를 수행한다. 임계값은 80%이다.

preProc <- preProcess(training[, IL_str], method = "pca", thresh = 0.8)


실행 결과 총 12개의 IL 변수중 7개만으로 원래 데이터 분포의 80%수준의 분산을 설명 할 수 있음을 알 수 있다.

preProc$rotation

                      PC1           PC2           PC3          PC4         PC5          PC6         PC7

IL_11         -0.06529786  0.5555956867  0.2031317937 -0.050389599  0.73512798 -0.102014559  0.20984151

IL_13          0.27529157  0.3559427297 -0.0399010765  0.265076920 -0.25796332 -0.068927711  0.58942516

IL_16          0.42079000  0.0007224953  0.0832211446 -0.082097273  0.04435883 -0.007094672 -0.06581741

IL_17E        -0.01126118  0.5635958176  0.3744707126  0.302512329 -0.38918707  0.221149380 -0.46462692

IL_1alpha      0.25078195 -0.0687043488 -0.3008366900  0.330945942  0.16992452  0.742391473  0.12787035

IL_3           0.42026485 -0.0703352892 -0.1049647272 -0.065352774  0.02352819 -0.165587911 -0.09006656

IL_4           0.33302031  0.0688495706 -0.1395450144  0.165631691 -0.14268797 -0.297421293  0.19661173

IL_5           0.38706503 -0.0039619980  0.0005616126 -0.224448981  0.08426042  0.153835977 -0.16425757

IL_6           0.05398185 -0.4248425653  0.6090821756  0.417591202 -0.00165066 -0.166089521  0.21895103

IL_6_Receptor  0.21218980  0.1005338329  0.2920341087 -0.659953479 -0.29654048  0.138000448  0.22657846

IL_7           0.32948731  0.0806070090 -0.1966471906  0.165544952  0.11373532 -0.405698338 -0.42065832

IL_8           0.29329723 -0.1883039842  0.4405255221  0.002811187  0.28608600  0.184321013 -0.14833779



이전과 같이 princomp를 수행하면 아래와 같다.

summary(princomp(training[, IL_str]))

                          Comp.1    Comp.2    Comp.3     Comp.4    Comp.5     Comp.6     Comp.7     Comp.8     Comp.9    Comp.10      Comp.11
Standard deviation     1.4947237 1.2328654 1.1784414 0.62482551 0.5437811 0.40042942 0.36239787 0.30216932 0.25401803 0.25223488 0.0286080996
Proportion of Variance 0.3523411 0.2397026 0.2190067 0.06156856 0.0466326 0.02528678 0.02071156 0.01439933 0.01017585 0.01003348 0.0001290683
Cumulative Proportion  0.3523411 0.5920437 0.8110505 0.87261902 0.9192516 0.94453840 0.96524995 0.97964928 0.98982513 0.99985861 0.9999876814
                            Comp.12
Standard deviation     8.838105e-03
Proportion of Variance 1.231856e-05
Cumulative Proportion  1.000000e+00



PCA가 정확도에 미치는 영향을 glm 모델을 만들어서 알아보자 [Practical Machine Learning HW-2]



set.seed(3433)

library(AppliedPredictiveModeling)

data(AlzheimerDisease)

adData = data.frame(diagnosis, predictors)

inTrain = createDataPartition(adData$diagnosis, p = 3/4)[[1]]

training = adData[inTrain, ]

testing = adData[-inTrain, ]


set.seed(3433)

## grep the predictors starting with 'IL'

IL_str <- grep("^IL", colnames(training), value = TRUE)

## make a subset of these predictors

predictors_IL <- predictors[, IL_str]

df <- data.frame(diagnosis, predictors_IL)

inTrain = createDataPartition(df$diagnosis, p = 3/4)[[1]]

training = df[inTrain, ]

testing = df[-inTrain, ]



PCA를 적용하지 않은 glm 모델

## train the data using the first method

modelFit <- train(diagnosis ~ ., method = "glm", data = training)

predictions <- predict(modelFit, newdata = testing)

## get the confustion matrix for the first method

C1 <- confusionMatrix(predictions, testing$diagnosis)

print(C1)


Confusion Matrix and Statistics


          Reference

Prediction Impaired Control

  Impaired        2       9

  Control        20      51

                                         

               Accuracy : 0.6463         

                 95% CI : (0.533, 0.7488)

    No Information Rate : 0.7317         

    P-Value [Acc > NIR] : 0.96637        

                                         

                  Kappa : -0.0702        

 Mcnemar's Test P-Value : 0.06332        

                                         

            Sensitivity : 0.09091        

            Specificity : 0.85000        

         Pos Pred Value : 0.18182        

         Neg Pred Value : 0.71831        

             Prevalence : 0.26829        

         Detection Rate : 0.02439        

   Detection Prevalence : 0.13415        

      Balanced Accuracy : 0.47045        

                                         

       'Positive' Class : Impaired 


PCA를 적용해서 수행한 결과이다.

# prediction with PCA

A1 <- C1$overall[1]


## do similar steps with the caret package

modelFit <- train(training$diagnosis ~ ., method = "glm", preProcess = "pca", 

                  data = training, trControl = trainControl(preProcOptions = list(thresh = 0.8)))

C2 <- confusionMatrix(testing$diagnosis, predict(modelFit, testing))

print(C2)


Confusion Matrix and Statistics


          Reference

Prediction Impaired Control

  Impaired        3      19

  Control         4      56

                                          

               Accuracy : 0.7195          

                 95% CI : (0.6094, 0.8132)

    No Information Rate : 0.9146          

    P-Value [Acc > NIR] : 1.000000        

                                          

                  Kappa : 0.0889          

 Mcnemar's Test P-Value : 0.003509        

                                          

            Sensitivity : 0.42857         

            Specificity : 0.74667         

         Pos Pred Value : 0.13636         

         Neg Pred Value : 0.93333         

             Prevalence : 0.08537         

         Detection Rate : 0.03659         

   Detection Prevalence : 0.26829         

      Balanced Accuracy : 0.58762         

                                          

       'Positive' Class : Impaired      


적용전 0.65

적용후 0.72


결국 dimentionality reduction이 정확도 향상에도 기여한다는 것을 알 수 있다.





TensorFlow 개발환경 ( Eclipse + PyDev, Pycharm, Jupyter)



TensorFlow 설치법과 PyDev 설치법은 이전에 다뤘다.

원할한 개발을 위해서 PyDev에 TensorFlow를 연결하는것을 다룬다.


우선 필자는 TensorFlow를 Virtualenv에다가 설치 했다. VirtualEnv를 설치하는 이유는 아래와 같다.

Here's the short version: pip lets you install packages (Python libraries). Usually, you do not want to install packages globally, for the entire system, because they may conflict with each other. Instead, you want each Python project you create to have its own isolated ecosystem. Virtualenv provides this isolation. Virtualenvwrapper makes virtualenv nicer to use.


Even if you're not worried about conflicts, virtualenv can help you make sure your demo still works years from now (especially important if you care about reproducible research). The fact is that libraries aren't always perfectly backward-compatible or bug-free. You may upgrade a package and find that it breaks your project. The more time passes since you last ran a piece of code, the more likely it is to be broken. Using a virtualenv to freeze the dependencies is a safeguard against this problem.



PyDev의 VirtualEnv 설정 연결


PyDev와 TensorFlow 그리고 virtualenv가 모두 설치되었어야 한다.


Eclipse 메뉴에서

Preferences -> PyDev > Interpreters > Python 메뉴에서

New를 눌러서 Browse를 통해서 설정한 virtualenvs를 등록해준다.

<venv-name>/bin/python


등록할때 모든 종속 라이브러릴 같이 등록해준다. 팝업이 나오면 다 선택된 상태로 등록하면 된다.






프로젝트 생성 및 "Hellow Tensor"실행


아래와 같이 프로젝트를 생성 할 때 interpreter를 생성한 virtualenv용 python 환경으로 잘 선택해 줘야 한다.



그다음 Hello tensorflow 코드를 실행하면 정상적으로 실행 되는것을 알 수 있다.

'''
Created on Nov 17, 2015

@author: root
'''
import tensorflow as tf
hello = tf.constant('Hello, TensorFlow!')
sess = tf.Session()
print sess.run(hello)

a = tf.constant(10)
b = tf.constant(32)
print sess.run(a+b)





IntelliJ 기반의 Pycharm


설치방법: 이전포스트


실행 모습



아래와 같이 Tensorflow가 있는 virtualenv를 interpreter로 설정을 해주어야 한다.





웹기반 Jupyter로 실행하기


설치방법: 이전포스트




'AI > TensorFlow, PyTorch, Keras, Scikit' 카테고리의 다른 글

Softmax Function  (0) 2016.04.19
Neural Networks in XOR problem  (0) 2016.04.18
Logistic Regression  (0) 2016.04.17
Linear regression with TensorFlow  (0) 2016.04.17
TensorFlow 설치 및 예제 실행 (uBuntu)  (2) 2015.11.17

TensorFlow 설치 및 예제 실행 (uBuntu)


2015.11.9일에 공개된 오픈프로젝트 TensorFlow는 Google에서 공개한 DeepLearning을 위한 2세대 system이다.

TensorFlow의 내용은 이전 Post를 참조 한다.

이 시스템의 설치와 실행을 다룬다.



Python VirtualEnv에 기반한 설치 방법


필자가 사용하는 Desktop PC의 환경은 아래와 같다.


설치환경

uBuntu 14.04 64bit (LTS)

VMware based

python 2.7 (설치법 참조)


필자는 VirtualEnv-based Installation을 수행한다(VirtualEnv 설치).

VirtualEnv 환경을 python 자체가 지원하므로 고립된 수행 환경을 생성할 수 있는 이점이 있다. 문제 발생시에도 모든 사람이 같은 상황이므로 해결방법을 그대로 적용할 수 있는 장점이 있다.

#Next, set up a new virtualenv environment. To set it up in the directory ~/tensorflow, run:
$ virtualenv --system-site-packages ~/tensorflow
$ cd ~/tensorflow

tensorflow는 이제 가상환경이 저장되는 디렉터리가 된다.

--system-site-packages 옵션의 의미는 가상환경에게 global site-package의 접근 권한을 허용 하는 것이다.

아래의 경로에 해당 가상환경에만 적용되는 site-package들이 저장되어 있다.

~/tensorflow/lib/python2.7/site-packages

아래의 경로에는 해당 가상환경에서 사용 할 수 있는 python의 종류이다.

~/tensorflow/lib/


virtualenv를 활성화 시킨다.

$ source (설치경로)/bin/activate  # If using bash
source ./tensorflow/bin/activate #나의 경우 

비활성화는 deactivate라고 치면된다.


만약 csh 쉘을 쓴다면 아래의 명령어이다.

$ source bin/activate.csh  # If using csh

최종적으로 아래처럼 프롬프트 창의 이름 앞에 (tensorflow)가 들어가는 것을 볼 수 있다.

(tensorflow)root@jemin-virtual-machine:

실행된 virtualenv의 환경에서 TensorFlow를 설치한다.

설치 종류는 CPU-only version과 GPU-enabled version이 있으므로 자신이 원하는것을 선택하자.

필자는 일단 CUDA sdk를 설치하지 않았으므로 CPU-only를 설치했다.

# For CPU-only version
$ pip install https://storage.googleapis.com/tensorflow/linux/cpu/tensorflow-0.5.0-cp27-none-linux_x86_64.whl

# For GPU-enabled version (only install this version if you have the CUDA sdk installed)
$ pip install https://storage.googleapis.com/tensorflow/linux/gpu/tensorflow-0.5.0-cp27-none-linux_x86_64.whl

이렇게까지 하면 tensorFlow는 설치가 된것이다.

이제 간단한 예제를 실행해보자.


Hello, TensorFlow

Hello, TensorFlow
Python 2.7.6 (default, Jun 22 2015, 17:58:13) 
[GCC 4.8.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import tensorflow as tf
>>> hello = tf.constant("Hello, TensorFlow!")
>>> sess = tf.Session()
I tensorflow/core/common_runtime/local_device.cc:25] Local device intra op parallelism threads: 8
I tensorflow/core/common_runtime/local_session.cc:45] Local session inter op parallelism threads: 8
>>> print sess.run(hello)
Hello, TensorFlow!
>>> a = tf.constant(10)
>>> b = tf.constant(32)
>>> print sess.run(a+b)
42
(tensorflow)root@jemin-virtual-machine:~/tensorflow# 
위와 같이 실행되면, 정상적으로 설치가 된것이다.


삼차원 데이터를 100개 생성하고 그것에 딱 맞는 초평면을 생성하는 예제이다.
# Make 100 phony data points in NumPy.
x_data = np.float32(np.random.rand(2, 100)) # Random input
y_data = np.dot([0.100, 0.200], x_data) + 0.300

# Construct a linear model.
b = tf.Variable(tf.zeros([1]))
W = tf.Variable(tf.random_uniform([1, 2], -1.0, 1.0))
y = tf.matmul(W, x_data) + b

# Minimize the squared errors.
loss = tf.reduce_mean(tf.square(y - y_data))
optimizer = tf.train.GradientDescentOptimizer(0.5)
train = optimizer.minimize(loss)

# For initializing the variables.
init = tf.initialize_all_variables()

# Launch the graph
sess = tf.Session()
sess.run(init)

# Fit the plane.
for step in xrange(0, 201):
    sess.run(train)
    if step % 20 == 0:
        print step, sess.run(W), sess.run(b)

# Learns best fit is W: [[0.100  0.200]], b: [0.300]
I tensorflow/core/common_runtime/local_device.cc:25] Local device intra op parallelism threads: 8
I tensorflow/core/common_runtime/local_session.cc:45] Local session inter op parallelism threads: 8
Hello, TensorFlow!
42
0 [[ 0.3138563   0.52770293]] [-0.0221094]
20 [[ 0.17014106  0.30057704]] [ 0.20599112]
40 [[ 0.12187628  0.23032692]] [ 0.27126268]
60 [[ 0.10676718  0.20918694]] [ 0.29121917]
80 [[ 0.10208294  0.2027912 ]] [ 0.29731771]
100 [[ 0.10063919  0.20084961]] [ 0.29918078]
120 [[ 0.10019579  0.20025893]] [ 0.29974979]
140 [[ 0.1000599   0.20007896]] [ 0.2999236]
160 [[ 0.10001831  0.2000241 ]] [ 0.29997668]
180 [[ 0.1000056   0.20000736]] [ 0.29999286]
200 [[ 0.1000017   0.20000222]] [ 0.29999784]

위와 같이 점차 반복수행 하면서, best fit인 0.1, 0.2, 0.3에 근접하는 것을 알 수 있다.



신경망 모델링 실행

이제 TensorFlow를 이용해서 이제 DeepLearning 예제를 실행해 보자.

실행할 예제는 신경망 모델링에 관한것이다.


일단 gitHub에서 TensorFlow 소스코드를 복제(clone)해와야 한다.

tensorflow)root@jemin-virtual-machine: git clone https://github.com/tensorflow/tensorflow.git

정상적으로 다운이 완료 되면, 예제를 실행 해보자.

이 예제는 "LeNet -5 - like convolutional MNIST model"이라고 한다.

(tensorflow)$ cd tensorflow/models/image/mnist
(tensorflow)$ python convolutional.py



실행결과

Initialized!
Epoch 0.00
Minibatch loss: 12.054, learning rate: 0.010000
Minibatch error: 90.6%
Validation error: 84.6%
Epoch 0.12
Minibatch loss: 3.285, learning rate: 0.010000
Minibatch error: 6.2%
Validation error: 7.0%
Epoch 0.23
Minibatch loss: 3.473, learning rate: 0.010000
Minibatch error: 10.9%
Validation error: 3.7%
Epoch 0.35
Minibatch loss: 3.221, learning rate: 0.010000
Minibatch error: 4.7%
Validation error: 3.2%
Epoch 0.47
Minibatch loss: 3.193, learning rate: 0.010000
Minibatch error: 4.7%
Validation error: 2.7%
Epoch 0.58
Minibatch loss: 3.301, learning rate: 0.010000
Minibatch error: 9.4%
Validation error: 2.5%
Epoch 0.70
Minibatch loss: 3.203, learning rate: 0.010000
Minibatch error: 6.2%
Validation error: 2.8%
Epoch 0.81
Minibatch loss: 3.022, learning rate: 0.010000
Minibatch error: 4.7%
Validation error: 2.6%
Epoch 0.93
Minibatch loss: 3.131, learning rate: 0.010000
Minibatch error: 6.2%
Validation error: 2.1%
Epoch 1.05
Minibatch loss: 2.954, learning rate: 0.009500
Minibatch error: 3.1%
Validation error: 1.6%
Epoch 1.16
Minibatch loss: 2.854, learning rate: 0.009500
Minibatch error: 0.0%
Validation error: 1.8%
Epoch 1.28
Minibatch loss: 2.825, learning rate: 0.009500
Minibatch error: 1.6%
Validation error: 1.4%
Epoch 1.40
Minibatch loss: 2.938, learning rate: 0.009500
Minibatch error: 7.8%
Validation error: 1.5%
Epoch 1.51
Minibatch loss: 2.767, learning rate: 0.009500
Minibatch error: 0.0%
Validation error: 1.9%
Epoch 1.63
Minibatch loss: 2.771, learning rate: 0.009500
Minibatch error: 3.1%
Validation error: 1.4%
Epoch 1.75
Minibatch loss: 2.844, learning rate: 0.009500
Minibatch error: 4.7%
Validation error: 1.2%
Epoch 1.86
Minibatch loss: 2.694, learning rate: 0.009500
Minibatch error: 0.0%
Validation error: 1.3%
Epoch 1.98
Minibatch loss: 2.650, learning rate: 0.009500
Minibatch error: 0.0%
Validation error: 1.5%
Epoch 2.09
Minibatch loss: 2.667, learning rate: 0.009025
Minibatch error: 1.6%
Validation error: 1.4%
Epoch 2.21
Minibatch loss: 2.658, learning rate: 0.009025
Minibatch error: 1.6%
Validation error: 1.2%
Epoch 2.33
Minibatch loss: 2.640, learning rate: 0.009025
Minibatch error: 3.1%
Validation error: 1.2%
Epoch 2.44
Minibatch loss: 2.579, learning rate: 0.009025
Minibatch error: 1.6%
Validation error: 1.1%
Epoch 2.56
Minibatch loss: 2.568, learning rate: 0.009025
Minibatch error: 0.0%
Validation error: 1.2%
Epoch 2.68
Minibatch loss: 2.554, learning rate: 0.009025
Minibatch error: 1.6%
Validation error: 1.1%
Epoch 2.79
Minibatch loss: 2.503, learning rate: 0.009025
Minibatch error: 0.0%
Validation error: 1.2%
Epoch 2.91
Minibatch loss: 2.487, learning rate: 0.009025
Minibatch error: 0.0%
Validation error: 1.2%
Epoch 3.03
Minibatch loss: 2.463, learning rate: 0.008574
Minibatch error: 1.6%
Validation error: 1.2%
Epoch 3.14
Minibatch loss: 2.458, learning rate: 0.008574
Minibatch error: 1.6%
Validation error: 1.1%
Epoch 3.26
Minibatch loss: 2.410, learning rate: 0.008574
Minibatch error: 0.0%
Validation error: 1.4%
Epoch 3.37
Minibatch loss: 2.496, learning rate: 0.008574
Minibatch error: 3.1%
Validation error: 1.3%
Epoch 3.49
Minibatch loss: 2.399, learning rate: 0.008574
Minibatch error: 1.6%
Validation error: 1.1%
Epoch 3.61
Minibatch loss: 2.377, learning rate: 0.008574
Minibatch error: 0.0%
Validation error: 1.1%
Epoch 3.72
Minibatch loss: 2.333, learning rate: 0.008574
Minibatch error: 0.0%
Validation error: 1.1%
Epoch 3.84
Minibatch loss: 2.312, learning rate: 0.008574
Minibatch error: 0.0%
Validation error: 1.2%
Epoch 3.96
Minibatch loss: 2.300, learning rate: 0.008574
Minibatch error: 1.6%
Validation error: 1.1%
Epoch 4.07
Minibatch loss: 2.276, learning rate: 0.008145
Minibatch error: 0.0%
Validation error: 1.1%
Epoch 4.19
Minibatch loss: 2.250, learning rate: 0.008145
Minibatch error: 0.0%
Validation error: 1.0%
Epoch 4.31
Minibatch loss: 2.233, learning rate: 0.008145
Minibatch error: 0.0%
Validation error: 1.0%
Epoch 4.42
Minibatch loss: 2.217, learning rate: 0.008145
Minibatch error: 0.0%
Validation error: 0.9%
Epoch 4.54
Minibatch loss: 2.324, learning rate: 0.008145
Minibatch error: 3.1%
Validation error: 1.0%
Epoch 4.65
Minibatch loss: 2.212, learning rate: 0.008145
Minibatch error: 0.0%
Validation error: 1.0%
Epoch 4.77
Minibatch loss: 2.174, learning rate: 0.008145
Minibatch error: 0.0%
Validation error: 0.9%
Epoch 4.89
Minibatch loss: 2.211, learning rate: 0.008145
Minibatch error: 1.6%
Validation error: 1.0%
Epoch 5.00
Minibatch loss: 2.193, learning rate: 0.007738
Minibatch error: 1.6%
Validation error: 1.0%
Epoch 5.12
Minibatch loss: 2.148, learning rate: 0.007738
Minibatch error: 3.1%
Validation error: 1.0%
Epoch 5.24
Minibatch loss: 2.153, learning rate: 0.007738
Minibatch error: 3.1%
Validation error: 1.0%
Epoch 5.35
Minibatch loss: 2.111, learning rate: 0.007738
Minibatch error: 1.6%
Validation error: 0.9%
Epoch 5.47
Minibatch loss: 2.084, learning rate: 0.007738
Minibatch error: 1.6%
Validation error: 0.8%
Epoch 5.59
Minibatch loss: 2.054, learning rate: 0.007738
Minibatch error: 0.0%
Validation error: 1.0%
Epoch 5.70
Minibatch loss: 2.043, learning rate: 0.007738
Minibatch error: 0.0%
Validation error: 1.0%
Epoch 5.82
Minibatch loss: 2.134, learning rate: 0.007738
Minibatch error: 3.1%
Validation error: 1.0%
Epoch 5.93
Minibatch loss: 2.006, learning rate: 0.007738
Minibatch error: 0.0%
Validation error: 1.0%
Epoch 6.05
Minibatch loss: 2.048, learning rate: 0.007351
Minibatch error: 3.1%
Validation error: 0.9%
Epoch 6.17
Minibatch loss: 1.988, learning rate: 0.007351
Minibatch error: 0.0%
Validation error: 1.1%
Epoch 6.28
Minibatch loss: 1.957, learning rate: 0.007351
Minibatch error: 0.0%
Validation error: 0.8%
Epoch 6.40
Minibatch loss: 1.971, learning rate: 0.007351
Minibatch error: 0.0%
Validation error: 0.9%
Epoch 6.52
Minibatch loss: 1.927, learning rate: 0.007351
Minibatch error: 0.0%
Validation error: 0.9%
Epoch 6.63
Minibatch loss: 1.912, learning rate: 0.007351
Minibatch error: 0.0%
Validation error: 1.0%
Epoch 6.75
Minibatch loss: 1.901, learning rate: 0.007351
Minibatch error: 0.0%
Validation error: 0.8%
Epoch 6.87
Minibatch loss: 1.886, learning rate: 0.007351
Minibatch error: 0.0%
Validation error: 0.8%
Epoch 6.98
Minibatch loss: 1.894, learning rate: 0.007351
Minibatch error: 1.6%
Validation error: 1.0%
Epoch 7.10
Minibatch loss: 1.859, learning rate: 0.006983
Minibatch error: 0.0%
Validation error: 0.8%
Epoch 7.21
Minibatch loss: 1.844, learning rate: 0.006983
Minibatch error: 0.0%
Validation error: 0.9%
Epoch 7.33
Minibatch loss: 1.836, learning rate: 0.006983
Minibatch error: 0.0%
Validation error: 1.0%
Epoch 7.45
Minibatch loss: 1.887, learning rate: 0.006983
Minibatch error: 3.1%
Validation error: 0.9%
Epoch 7.56
Minibatch loss: 1.808, learning rate: 0.006983
Minibatch error: 0.0%
Validation error: 0.8%
Epoch 7.68
Minibatch loss: 1.822, learning rate: 0.006983
Minibatch error: 1.6%
Validation error: 0.9%
Epoch 7.80
Minibatch loss: 1.782, learning rate: 0.006983
Minibatch error: 0.0%
Validation error: 0.9%
Epoch 7.91
Minibatch loss: 1.772, learning rate: 0.006983
Minibatch error: 0.0%
Validation error: 0.9%
Epoch 8.03
Minibatch loss: 1.761, learning rate: 0.006634
Minibatch error: 0.0%
Validation error: 0.9%
Epoch 8.15
Minibatch loss: 1.773, learning rate: 0.006634
Minibatch error: 1.6%
Validation error: 0.9%
Epoch 8.26
Minibatch loss: 1.742, learning rate: 0.006634
Minibatch error: 0.0%
Validation error: 0.9%
Epoch 8.38
Minibatch loss: 1.744, learning rate: 0.006634
Minibatch error: 0.0%
Validation error: 0.9%
Epoch 8.49
Minibatch loss: 1.719, learning rate: 0.006634
Minibatch error: 0.0%
Validation error: 0.8%
Epoch 8.61
Minibatch loss: 1.700, learning rate: 0.006634
Minibatch error: 0.0%
Validation error: 0.9%
Epoch 8.73
Minibatch loss: 1.700, learning rate: 0.006634
Minibatch error: 0.0%
Validation error: 0.8%
Epoch 8.84
Minibatch loss: 1.801, learning rate: 0.006634
Minibatch error: 1.6%
Validation error: 0.8%
Epoch 8.96
Minibatch loss: 1.666, learning rate: 0.006634
Minibatch error: 0.0%
Validation error: 0.9%
Epoch 9.08
Minibatch loss: 1.666, learning rate: 0.006302
Minibatch error: 0.0%
Validation error: 0.9%
Epoch 9.19
Minibatch loss: 1.649, learning rate: 0.006302
Minibatch error: 0.0%
Validation error: 0.8%
Epoch 9.31
Minibatch loss: 1.676, learning rate: 0.006302
Minibatch error: 1.6%
Validation error: 0.8%
Epoch 9.43
Minibatch loss: 1.626, learning rate: 0.006302
Minibatch error: 0.0%
Validation error: 0.8%
Epoch 9.54
Minibatch loss: 1.621, learning rate: 0.006302
Minibatch error: 0.0%
Validation error: 0.9%
Epoch 9.66
Minibatch loss: 1.606, learning rate: 0.006302
Minibatch error: 0.0%
Validation error: 0.8%
Epoch 9.77
Minibatch loss: 1.596, learning rate: 0.006302
Minibatch error: 0.0%
Validation error: 0.9%
Epoch 9.89
Minibatch loss: 1.602, learning rate: 0.006302
Minibatch error: 0.0%
Validation error: 0.9%
Test error: 0.8%

실행 시간은 아래와 같다.

- MNIST -

real 26m26.382s

user 77m58.714s

sys 19m21.539s


가상화 환경에서 CPU만 가지고 처리해서 오래걸린것 같다.

그래도 i7 4세대 프로세서, ram 24gb, ssd pro 256gb에서 실행한것인데, 대략 Android 5.0 컴파일 시간의 반 정도의 시간이 걸린것 같다.








Evaluating Model Performance with R


이론적인 부분은 아래의 이전 포스트를 확인한다.




Confusion Matrix


가장 쉽게 생성할 수 있는 방법은 table()을 이용 하는 것이다.

vec1 <- factor(c("ham","ham","spam","ham"))
vec2 <- factor(c("spam","ham","spam","ham"))
> table(vec1,vec2)
      vec2
vec1   ham spam
  ham    2    1
  spam   0    1


좀더 상세한 결과를 원한다면, gmodels package의 CrossTable() function을 사용하자.

default로써 CrossTable() output으로써 proportion값을 포함하고 있다.

> class(sms_test_pred)
[1] "factor"
> class(sms_raw_test$type)
[1] "factor"
> CrossTable(sms_test_pred, sms_raw_test$type)

 
   Cell Contents
|-------------------------|
|                       N |
| Chi-square contribution |
|           N / Row Total |
|           N / Col Total |
|         N / Table Total |
|-------------------------|

 
Total Observations in Table:  1390 

 
              | sms_raw_test$type 
sms_test_pred |       ham |      spam | Row Total | 
--------------|-----------|-----------|-----------|
          ham |      1202 |        31 |      1233 | 
              |    16.109 |   106.250 |           | 
              |     0.975 |     0.025 |     0.887 | 
              |     0.996 |     0.169 |           | 
              |     0.865 |     0.022 |           | 
--------------|-----------|-----------|-----------|
         spam |         5 |       152 |       157 | 
              |   126.514 |   834.437 |           | 
              |     0.032 |     0.968 |     0.113 | 
              |     0.004 |     0.831 |           | 
              |     0.004 |     0.109 |           | 
--------------|-----------|-----------|-----------|
 Column Total |      1207 |       183 |      1390 | 
              |     0.868 |     0.132 |           | 
--------------|-----------|-----------|-----------|

위 데이터를 가지고 직접 precision, recall, accuracy, error rate 등을 계산 하면 된다.





Beyond accuracy, other measures of performance


자동 계산을 위해서는 또 다른 package인 caret을 사용 한다.

해당 package는 classificationregression training에 대한 정보를 담고 있으며, Max Kuhn에 의해서 만들어 졌다.


preparing, training, evaluating, and visualizing machine learning models and data 등을 지원 한다.


Journal of statistical software에 "Building predictive models in R using the caret package."을 보면 상세한 내용을 알 수 있다.


아래와 같이 사용하며, 단지 positive label이 무엇인지만 정의해 주면 된다. 

> library(caret)
필요한 패키지를 로딩중입니다: lattice
필요한 패키지를 로딩중입니다: ggplot2
> confusionMatrix(sms_test_pred, sms_raw_test$type, positive = "spam")
Confusion Matrix and Statistics

          Reference
Prediction  ham spam
      ham  1202   31
      spam    5  152
                                          
               Accuracy : 0.9741          
                 95% CI : (0.9643, 0.9818)
    No Information Rate : 0.8683          
    P-Value [Acc > NIR] : < 2.2e-16       
                                          
                  Kappa : 0.8795          
 Mcnemar's Test P-Value : 3.091e-05       
                                          
            Sensitivity : 0.8306          
            Specificity : 0.9959          
         Pos Pred Value : 0.9682          
         Neg Pred Value : 0.9749          
             Prevalence : 0.1317          
         Detection Rate : 0.1094          
   Detection Prevalence : 0.1129          
      Balanced Accuracy : 0.9132          
                                          
       'Positive' Class : spam    


이제 일반적인 statistics를 알아보자.


■ The kappa statistic





■ Leave One Out Cross - Validation


cross validation 방법의 일종으로써


총 N개의 샘플을 생성 한다.

이때 N개는 해당 데이터 셋의 샘플들의 총 수를 말한다

즉, 100개의 데이터 샘플이 있다면 N은 100이다.


이에대해서 이제 N개의 모델을 생성 한다. 그리고 각각의 모델을 생성할때

하나의 샘플씩을 제외한다. 즉 N개의 모델은 N-1개의 샘플 수로 만들어지는 것이다.


그리고 각각의 모델에 대해서 그 제외한 샘플 1개를 이용해서 test를 수행한다.

그렇게 나온 test 결과 N개에대한 평균 값을 구하게 된다.



MSE가 측정 방법이라고 한다면 아래의 식과 같다.

$MSE_{ i }=(y_{ 2 }-\hat { y_{ 2 } } )^{ 2 }$


구하려고 하는 성능의 척도는 결국 아래와 같다.

$CV_{(n)}=\frac{1}{n}\sum_{i=1}^{n}{MSE_{i}}$



LOOCV의 장점은 결국 모든 샘플에 대해서 다 한번씩은 test하는 것이 되므로

어떠한 랜덤성도 존재하지 않게 된다. 따라서 굉장히 stable한 결과를 얻을 수 있다.


단점으로는 N개의 모델을 만들어서 N번 테스트 하게 되므로 computing time이 굉장히 오래 걸리게 된다.

또한 K-fold Cross validation에 비해서 bias의 비중이 높게 된다. 증 다양성을 포함하기 어렵게 된다.


아래는 작업을 수행한 코드이다.

# LOOCV 처리방법
library(ISLR)
glm.fit=glm(mpg~horsepower, data=Auto) #mpg를 horsepower에 대해서 linear regression을 힙니다.
library(boot) #cv.glm을 사용하기 위한 라이브러리입니다.
cv.err=cv.glm(Auto, glm.fit) #cv.glm함수를 통해 LOOCV를 시행합니다.
cv.err$delta #delta는 cross-validation 결과들을 담고 있습니다.

#linear model, polynomial model에 한해서 LOOCV에 간단한 형태의 공식을 사용할 수 있습니다.
loocv=function(fit){
    h=lm.influence(fit)$h
    mean((residuals(fit)/(1-h))^2)
}

loocv(glm.fit)

cv.error=rep(0,5)
degree=1:5 #1차부터 5차함수까지 fitting해 봅시다.
for(d in degree){
    glm.fit=glm(mpg~poly(horsepower,d), data=Auto) #poly함수를 이용해 d차 함수를 fit할 수 있습니다.
    cv.error[d]=loocv(glm.fit) #LOOCV 결과를 각 벡터에 저장합니다.
}
plot(degree,cv.error,type="b") #LOOCV 결과를 plot합니다.



5차원에서 가장 에러가 적은것을 알 수 있다.


자료출처: https://smlee729.github.io/r/machine%20learning/2015/03/19/1-loocv.html



















Naive Bayes with Caret package


이전 포스트에서 e1071 package로 구현 했던 것을 다시 caret package로 구현 한다.
caret package에는 많은 기능들이 포함되어 있으고 더 강력한것 같다.

Preliminary information

data set은 같은 것을 사용함.

  • 추가 package 사용 목록
  • caret package 사용
  • tm package 사용
  • pander package 사용: 출력을 보기좋게 표로 나타내줌.
  • doMC package: 멀티코어 병렬처리 가능. 하지만 내부적으로 Fork()를 이용해서 구현했기 때문에 Linux 환경에서만 동작 한다.
  • 몇가지 도우미 함수들 정의

Import Packages

# libraries needed by caret
library(klaR)
library(MASS)
# for the Naive Bayes modelling
library(caret)
# to process the text into a corpus
library(tm)
# to get nice looking tables
library(pander)
# to simplify selections
library(dplyr)

# doMC package use fork() internally. so, it it not working in Window OS.
#library(doMC)
#registerDoMC(cores=4)

# a utility function for % freq tables
frqtab <- function(x, caption) {
    round(100*prop.table(table(x)), 1)
}

# utility function to summarize model comparison results
sumpred <- function(cm) {
    summ <- list(TN=cm$table[1,1],  # true negatives
                 TP=cm$table[2,2],  # true positives
                 FN=cm$table[1,2],  # false negatives
                 FP=cm$table[2,1],  # false positives
                 acc=cm$overall["Accuracy"],  # accuracy
                 sens=cm$byClass["Sensitivity"],  # sensitivity
                 spec=cm$byClass["Specificity"])  # specificity
    lapply(summ, FUN=round, 2)
}

Reading and Preparing the data

다운로드 받아서 수행하는 것은 잘 안된다.

이전 POST의 e1071에 업로드한 csv 파일을 다운받아서 
데이터를 R object로 생성하는것은 똑같이 진행 한다.

# read the sms data into the sms data frame
sms_raw <- read.csv("sms_spam.csv", stringsAsFactors = FALSE)

sms_raw[1072,"text"] <- c("All done, all handed in. Don't know if mega shop in asda counts as celebration but thats what i'm doing!")
sms_raw[1072,"type"] <- c("ham")

# examine the structure of the sms data
str(sms_raw)

# convert spam/ham to factor.
sms_raw$type <- factor(sms_raw$type)

# examine the type variable more carefully
str(sms_raw$type)
table(sms_raw$type)

# build a corpus using the text mining (tm) package
library(tm)

# try to encode again to UTF-8
sms_raw$text <- iconv(enc2utf8(sms_raw$text),sub="byte")

sms_corpus <- Corpus(VectorSource(sms_raw$text))

# examine the sms corpus
print(sms_corpus)
inspect(sms_corpus[1:3])

Output

<<VCorpus>>
Metadata:  corpus specific: 0, document level (indexed): 0
Content:  documents: 3

[[1]]
<<PlainTextDocument>>
Metadata:  7
Content:  chars: 49

[[2]]
<<PlainTextDocument>>
Metadata:  7
Content:  chars: 23

[[3]]
<<PlainTextDocument>>
Metadata:  7
Content:  chars: 43

Preparing the data

이전에 수행한 방식과 마찬가지로 data cleanup 및 transformation을 수행한다.
이때 dplyr 문법을 사용해서 작업을 수행 한다.
아래의 명령어를 수행하면 상용법을 알수 있다.

browseVignettes(package = "dplyr")
# ------------------------------------
# ------------------------------------
# We will proceed in similar fashion as described in the book, but make use of "dplyr"
# syntax to execute the text cleanup / transformation operations
sms_corpus <- Corpus(VectorSource(sms_raw$text))
sms_corpus_clean <- tm_map(sms_corpus, content_transformer(tolower))
sms_corpus_clean <- tm_map(sms_corpus_clean, removeNumbers)
sms_corpus_clean <- tm_map(sms_corpus_clean, removeWords, stopwords())
sms_corpus_clean <- tm_map(sms_corpus_clean, removePunctuation)
sms_corpus_clean <- tm_map(sms_corpus_clean, stripWhitespace)

sms_dtm <- DocumentTermMatrix(sms_corpus_clean)

Creating a Classification model with Naive Bayes

1. Generating the training and testing datasets

데이터를 분할하고
분할한 training set과 test set 모두에서 spam의 비율을 똑같이 설정 한다.
이전과 다른것은 Data 분할을 createDataPartition()을 이용해서 분할 한다는 것이다.

# -----------------------------------
# Creating a classificiation model with Naive Bayes
# Generating the training and testing datasets
train_index <- createDataPartition(sms_raw$type, p=0.75, list=FALSE)
sms_raw_train <- sms_raw[train_index,]
sms_raw_test <- sms_raw[-train_index,]
sms_corpus_clean_train <- sms_corpus_clean[train_index]
sms_corpus_clean_test <- sms_corpus_clean[-train_index]
sms_dtm_train <- sms_dtm[train_index,]
sms_dtm_test <- sms_dtm[-train_index,]

# check proportions in the testing and training sets 
ft_orig <- frqtab(sms_raw$type)
ft_train <- frqtab(sms_raw_train$type)
ft_test <- frqtab(sms_raw_test$type)
ft_df <- as.data.frame(cbind(ft_orig, ft_train, ft_test))
colnames(ft_df) <- c("Original", "Training set", "Test set")
pander(ft_df, style="rmarkdown",
       caption=paste0("Comparison of SMS type frequencies among datasets"))

|       |  Original  |  Training set  |  Test set  |
|:----------:|:----------:|:--------------:|:----------:|
|  **ham**   |    86.6    |      86.5      |    86.6    |
|  **spam**  |    13.4    |      13.5      |    13.4    |

Table: Comparison of SMS type frequencies among datasets

일정 이상 빈도인 것들만 추려내며
Absent 와 Present 이진데이터로 변경 한다.

# we will pick terms that appear at least 5 times in the training doucment term matrix.
sms_dict <- findFreqTerms(sms_dtm_train, lowfreq=5)
sms_train <- DocumentTermMatrix(sms_corpus_clean_train, list(dictionary=sms_dict))
sms_test <- DocumentTermMatrix(sms_corpus_clean_test, list(dictionary=sms_dict))
# modified sligtly fron the code in the book
convert_counts <- function(x) {
    x <- ifelse(x > 0, 1, 0)
    x <- factor(x, levels = c(0, 1), labels = c("Absent", "Present"))
}
sms_train <- sms_train %>% apply(MARGIN=2, FUN=convert_counts)
sms_test <- sms_test %>% apply(MARGIN=2, FUN=convert_counts)

2. Training the two prediction models

Naive Bayes 을 이용해서 기계학습을 수행 한다.
생성은 10-fold cross validation을 사용해서 한다.

그리고 두개의 모델을 각각 나눠서 수행 한다.
각각의 모델은 라플라스 추정자와 kernel density를 각각 적용한 것과 그렇지 않은것으로 구분 한다.

우선 Caret pakcage에서 Naive Bayes 모델에 대해서 지원하는 Control parameter의 종류는 아래와 같다.

> modelLookup("nb")
  model parameter              label forReg forClass probModel
1    nb        fL Laplace Correction  FALSE     TRUE      TRUE
2    nb usekernel  Distribution Type  FALSE     TRUE      TRU

위와 같이 modelLookup()명령어를 통해서 필요한 Machine Learning모델의 파라메터 지원의 모든 종류를 확인 할 수 있다.

아래는 라플라스 조정자를 적용하지 않은것이다. default 값을 의미한다.

ctrl <- trainControl(method="cv", 10)
set.seed(12358)
sms_model1 <- train(sms_train, sms_raw_train$type, method="nb", trControl=ctrl)
sms_model1

Naive Bayes 

4170 samples
1234 predictors
   2 classes: 'ham', 'spam' 

No pre-processing
Resampling: Cross-Validated (10 fold) 
Summary of sample sizes: 3753, 3753, 3754, 3753, 3753, 3753, ... 
Resampling results across tuning parameters:

  usekernel  Accuracy   Kappa      Accuracy SD  Kappa SD  
  FALSE      0.9798567  0.9098827  0.003606845  0.01680031
   TRUE      0.9798567  0.9098827  0.003606845  0.01680031

Tuning parameter 'fL' was held constant at a value of 0
Accuracy was used to select the optimal model using  the largest value.
The final values used for the model were fL = 0 and usekernel = FALSE.

아래는 라플라스 조정자를 1로 설정하고 kernel density는 적용하지 않았다.

set.seed(12358)
sms_model2 <- train(sms_train, sms_raw_train$type, method="nb", 
                    tuneGrid=data.frame(.fL=1, .usekernel=FALSE),
                    trControl=ctrl)

> sms_model2
Naive Bayes 

4170 samples
1234 predictors
   2 classes: 'ham', 'spam' 

No pre-processing
Resampling: Cross-Validated (10 fold) 
Summary of sample sizes: 3753, 3753, 3754, 3753, 3753, 3753, ... 
Resampling results

  Accuracy   Kappa      Accuracy SD  Kappa SD  
  0.9793765  0.9071502  0.004822776  0.02320774

Tuning parameter 'fL' was held constant at a value of 1
Tuning parameter 'usekernel' was held constant at a value
 of FALSE

Testing the predictions

두개의 모델을 confusionMatrix()을 이용해서 평가한다.
positive result의 목적은 message가 SPAM으로 prediction 됬을 때를 의미한다.

confusionMatrix()의 사용법은 positive label만 설정해주고 두개의 비교할 라벨 데이터만 넣어주면된다.

# Testing the predictions
sms_predict1 <- predict(sms_model1, sms_test)
cm1 <- confusionMatrix(sms_predict1, sms_raw_test$type, positive="spam")
cm1
Confusion Matrix and Statistics

          Reference
Prediction  ham spam
      ham  1199   25
      spam    4  161

               Accuracy : 0.9791         
                 95% CI : (0.9702, 0.986)
    No Information Rate : 0.8661         
    P-Value [Acc > NIR] : < 2.2e-16      

                  Kappa : 0.9055         
 Mcnemar's Test P-Value : 0.0002041      

            Sensitivity : 0.8656         
            Specificity : 0.9967         
         Pos Pred Value : 0.9758         
         Neg Pred Value : 0.9796         
             Prevalence : 0.1339         
         Detection Rate : 0.1159         
   Detection Prevalence : 0.1188         
      Balanced Accuracy : 0.9311         

       'Positive' Class : spam 

Laplace estimator 설정 kernel density를 적용하지 않은 모델의 경우

> sms_predict2 <- predict(sms_model2, sms_test)
There were 50 or more warnings (use warnings() to see the first 50)
> cm2 <- confusionMatrix(sms_predict2, sms_raw_test$type, positive="spam")
> cm2
Confusion Matrix and Statistics

          Reference
Prediction  ham spam
      ham  1200   27
      spam    3  159

               Accuracy : 0.9784          
                 95% CI : (0.9693, 0.9854)
    No Information Rate : 0.8661          
    P-Value [Acc > NIR] : < 2.2e-16       

                  Kappa : 0.9015          
 Mcnemar's Test P-Value : 2.679e-05       

            Sensitivity : 0.8548          
            Specificity : 0.9975          
         Pos Pred Value : 0.9815          
         Neg Pred Value : 0.9780          
             Prevalence : 0.1339          
         Detection Rate : 0.1145          
   Detection Prevalence : 0.1166          
      Balanced Accuracy : 0.9262          

       'Positive' Class : spam            

라플라스 추정자를 설정하지 않을경우 0 확률이 존재해서 데이터가 작다면 매우 부정확해 질 수 있다. 즉 어느 하나가 0%이기 때문에 다른것에 상관없이 무조건 0 probablity가 나와 버린다.

caret based Naive Bayes와 e1071 based Naive Bayes의 비교

TP, TN, FP, FN
accuracy
sensitivity (also known as recall or true positive rate)
specificity (also known as true negative rate)

> # from the table on page 115 of the book
> tn=1203
> tp=151
> fn=32
> fp=4
> book_example1 <- list(
+     TN=tn,
+     TP=tp,
+     FN=fn,
+     FP=fp,
+     acc=(tp + tn)/(tp + tn + fp + fn),
+     sens=tp/(tp + fn),
+     spec=tn/(tn + fp))
> 
> # from the table on page 116 of the book
> tn=1204
> tp=152
> fn=31
> fp=3
> book_example2 <- list(
+     TN=tn,
+     TP=tp,
+     FN=fn,
+     FP=fp,
+     acc=(tp + tn)/(tp + tn + fp + fn),
+     sens=tp/(tp + fn),
+     spec=tn/(tn + fp))
> 
> b1 <- lapply(book_example1, FUN=round, 2)
> b2 <- lapply(book_example2, FUN=round, 2)
> m1 <- sumpred(cm1)
> m2 <- sumpred(cm2)
> model_comp <- as.data.frame(rbind(b1, b2, m1, m2))
> rownames(model_comp) <- c("Book model 1", "Book model 2", "Caret model 1", "Caret model 2")
> pander(model_comp, style="rmarkdown", split.tables=Inf, keep.trailing.zeros=TRUE,
+        caption="Model results when comparing predictions and test set")


|                |  TN  |  TP  |  FN  |  FP  |  acc  |  sens  |  spec  |
|:-------------------:|:----:|:----:|:----:|:----:|:-----:|:------:|:------:|
|  **Book model 1**   | 1203 | 151  |  32  |  4   | 0.97  |  0.83  |   1    |
|  **Book model 2**   | 1204 | 152  |  31  |  3   | 0.98  |  0.83  |   1    |
|  **Caret model 1**  | 1199 | 161  |  25  |  4   | 0.98  |  0.87  |   1    |
|  **Caret model 2**  | 1200 | 159  |  27  |  3   | 0.98  |  0.85  |   1    |

Table: Model results when comparing predictions and test set

비교결과 accuracy에 대해서는 e1071와 caret 두 package는 별다른 차이가 없다.
하지만, sensitivity 측면에서는 차이가 있는 것을 알 수 있다.

추가내용 참조


Naive Bayes Classification with R


Bayesian rule에 대해서는 이전 theorem post를 참조 한다.

Bayes algorithm을 이용해서 mobile phone spam을 filtering하는 방법

Short Message Service (SMS)를 구분 하는 작업을 수행 한다.



Step 1 - collecting data


naive bayes classifer를 개발하기 위해서 SMS Spamcollection으로 부터 알고리즘을 적용 한다.

http://www.dt.fee.unicamp.br/~tiago/smsspamcollection/


이 스팸 데이터의 포맷은 아래와 같다.



spam 라벨은 junk message를 의미하고, ham 라벨은 legitimate message를 의미한다.


ham massage의 예:

  • Better. Made up for Friday and stuffed myself like a pig yesterday. Now I feel bleh. But at least its not writhing pain kind of bleh.
  • If he started searching he will get job in few days. He have great potential and talent.
  • I got another job! The one at the hospital doing data analysis or something, starts on monday! Not sure when my thesis will got finished


spam message의 예:

  • Congratulations ur awarded 500 of CD vouchers or 125gift guaranteed & Free entry 2 100 wkly draw txt MUSIC to 87066
  • December only! Had your mobile 11mths+? You are entitled to update to the latest colour camera mobile for Free! Call The Mobile Update Co FREE on 08002986906
  • Valentines Day Special! Win over £1000 in our quiz and take your partner on thetrip of a lifetime! Send GO to 83600 now. 150p/msg rcvd.
ham과 spam 메시지와의 차이는 우선 위의 메시지만을 고려해 볼 때,
세개 중 두개의 spam은 "free"라는 메시지를 사용 했다. 

이와 다르게, ham message의 경우 2개의 메시지들은 각각 상세한 날짜를 언급 하고 있다는 점이 다르다.

이러한 데이터에 naive bayes classifier를 적용할 경우, 위에서 언급한 단어의 발생 빈도를 이용해서 SMS message가 spam 인지 ham인지를 구분하게 된다.

단순히 free가 들어간 메시지를 spam으로 처리하는 것은 문제가 될 수 있다. 
왜냐하면, 같은 free라도
"are you free on Sunday?" 는 ham 이고
"free ringtones."는 spam 이다.
따라서 제안된 classifier는 확률을 이용해서 전체 메시지의 단어 빈도를 분석해서 spam 인지 아닌지를 결정 해야 한다.


Step 2 - exploring and preparing the data


text 파일을 처리가 어려움으로 csv 파일로 변경된 것을 사용 한다.

sms_spam.csv

#데이터 읽기
sms_raw <- read.csv("sms_spam.csv",stringsAsFactors =  FALSE)
#구조 분석 파악
> str(sms_raw)
'data.frame':	5559 obs. of  2 variables:
 $ type: chr  "ham" "ham" "ham" "spam" ...
 $ text: chr  "Hope you are having a good week. Just checking in" "K..give back my thanks." "Am also doing in cbe only. But have to pay." "complimentary 4 STAR Ibiza Holiday or 짙10,000 cash needs your URGENT collection. 09066364349 NOW from Landline not to lose out"| __truncated__ ...
#데이터 타입 분석
> class(sms_raw)
[1] "data.frame"

feature는 type과 text 두개로 구성되었다.
data frame의 구조를 가지며, 5559개의 열로 구성 되었다.

그리고 text feature에 SMS message의 raw 데이터가 기록 되어 있다.


현재 type은 stringAsFactors =  FALSE 로 설정 했기 때문에 factor가 아니라, character vector로 구성 되어 있다.
하지만, 이 type은 categorical variable 이기 때문에 factor로 변환 하는것이 좋다.
※ 처음 부터 factor로 만들지 않은 것은 text는 factor 타입보다는 character vector에 더 적합하기 때문이다.
#변경을 수행 한다.
> sms_raw$type <- factor(sms_raw$type)
#변경 결과를 확인하면 정상적으로 Factor가 된것을 알 수 있다.
> str(sms_raw)
'data.frame':	5559 obs. of  2 variables:
 $ type: Factor w/ 3 levels "All done, all handed in. Don't know if mega shop in asda counts as celebration but thats what i'm doing!",..: 2 2 2 3 3 2 2 2 3 2 ...
 $ text: chr  "Hope you are having a good week. Just checking in" "K..give back my thanks." "Am also doing in cbe only. But have to pay." "complimentary 4 STAR Ibiza Holiday or 짙10,000 cash needs your URGENT collection. 09066364349 NOW from Landline not to lose out"| __truncated__ ...
이상하게도 기본적으로 제공되는 csv 파일을 가지고 작업을 수행할 경우 문제가 발생한다.
따라서 아래의 코드 작업을 수행 해야 한다.
# 부족한 데이터를 삽입한다.
sms_raw[1072,"type"] <- factor("ham")
sms_raw[1072,"text"] <- c("All done, all handed in. Don't know if mega shop in asda counts as celebration but thats what i'm doing!")


부족한 데이터 삽입전에는 아래와 같이 요인이 3개가 된다. 하나가 요인 값이랑 text 값이 잘못 되어 있기 때문이다.

> table(sms_raw$type)
All done, all handed in. Don't know if mega shop in asda counts as celebration but thats what i'm doing! 
                                                                                                       1 
                                                                                                     ham 
                                                                                                    4811 
                                                                                                    spam 
                                                                                                     747

잘못된 데이터를 수정 하면 아래와 같이 바뀐다.

> table(sms_raw$type)
 ham spam 
4812  747 


■ Data preparation - processing text data for analysis


Document Classification은 주어진 문서를 하나 이상의 분류로 구분하는 문제이다.

지금 다루는 스팸 인지 아닌지를 구분하는 것이 문서 분류의 가장 흔한 예라고 할 수 있다.

또 다른 예로는 제품 리뷰 글을 보고 해당 리뷰가 제품에 대한 긍정적인 리뷰인지 부정적인 리뷰인지를 구분하는 

감성 분석 (Sentiment Analysis)이 있다.


text 처리를 위해서 특화된 package인 Text Mining (tm)의 사용에 대해서 다루 겠다.

TM은 Ingo Feinerer에 의해서 Vienna University에서 박사학위 논문의 일환으로 만들어진 Package 이다.

전공은 Economics and Business이다. 해당 package에 대해서 좀 더 깊이 있는 학습을 원할경우 아래의 사이트를 참조 하자.

http://tm.r-forge.r-project.org/


install.packages("tm")

library(tm)


우선 corpus function을 이용해서 text 데이터를 오브젝트로 생성 한다.


포멧팅 오류를 막기 위해서, corpus 실행전에 UTF-8 en-coding을 수행 하자.

그다음 corpus 함수를 수행 한다.

아래와 같이 corpus 함수는 text를 정의하고 있는 파라메터를 입력 받는다. 이를 위해서 text를 일련의 vector로 만들었었다. 이것을 넘겨줄때 VectorSource()를 이용해서 vector임을 corpus에게 알려주게 된다.

최종적으로 corpus는 sms_corpus라고 이름 지어진 Object를 반환하게 된다. 이 Object를 이용해서 이제 Text Data 처리를 쉽게 할 수 있다.

# try to encode again to UTF-8
sms_raw$text <- iconv(enc2utf8(sms_raw$text),sub="byte")
sms_corpus <- Corpus(VectorSource(sms_raw$text))
# examine the sms corpus
print(sms_corpus)
inspect(sms_corpus[1:3])
Metadata:  corpus specific: 0, document level (indexed): 0
Content:  documents: 3
[[1]]
Metadata:  7
Content:  chars: 49
[[2]]
Metadata:  7
Content:  chars: 23
[[3]]
Metadata:  7
Content:  chars: 43


Corpus는 정말로 유연한 문서 읽기 라이브러리이다. 

PDF나 Microsoft Word 문서 같은 것들을 읽을 수 있다. 좀 더 조사하고 싶다면, Data Input section에 대해서

tm package의 vignette를 이용해서 관련 문서를 확인하다.

command: print(vignette("tm"))


본격적으로 text를 분할해서 단어를 분석하기 전에 기본적으로 

데이터를 클린하는 것이 필요하다.

즉, punctuation과 의미 없는 단어들을 말한다.


각각의 단계는 아래와 같다.

# clean up the corpus using tm_map()
# 소문자로 모두 변경 한다.
corpus_clean <- tm_map(sms_corpus, content_transformer(tolower))
# 숫자를 모두 제거 한다.
corpus_clean <- tm_map(corpus_clean, removeNumbers)
# stop words로 알려진, "to", "and", "but", "or"를 모두 제거 한다.
# 이것을 위해서 stop word를 새로 정의하지 않고, 알려진 리스트인 stopword()를 이용 한다.
corpus_clean <- tm_map(corpus_clean, removeWords, stopwords())
# punctuation을 제거 한다.
corpus_clean <- tm_map(corpus_clean, removePunctuation)
# single space로 모두 만든다. 즉, whitespace(여백)을 모두 제거 한다.
corpus_clean <- tm_map(corpus_clean, stripWhitespace)


각각 변경된 내부 구조를 살펴 보면 아래와 같다.

> lapply(sms_corpus[1:3],as.character)
$`1`
[1] "Hope you are having a good week. Just checking in"
$`2`
[1] "K..give back my thanks."
$`3`
[1] "Am also doing in cbe only. But have to pay."
> lapply(corpus_clean[1:3],as.character)
$`1`
[1] "hope good week just checking "
$`2`
[1] "kgive back thanks"
$`3`
[1] " also cbe pay"


data pre-processing의 마지막 단계는 tokenization 단계이다.

해당 단계에서는 각각의 word를 하나의 컴포넌트로 구분하게 된다.


DocumentTermMatrix() 함수를 이용해서 sparse matrix를 구성 한다.

기본적으로 그냥 생성하면 SMS message를 나타내는 것은 5,559개 이며, term을 나타내는 것은 7,000개 이다.

> sms_dtm <- DocumentTermMatrix(corpus_clean)
> sms_dtm
<>
Non-/sparse entries: 42635/44407129
Sparsity           : 100%
Maximal term length: 40
Weighting          : term frequency (tf)

이제 sparse matrix을 생성 했으므로 word frequency를 수행 할 수 있다.



■ Data preparation - creating training and test datasets 

트레이닝과 테스트로 데이터를 나눠서 나중에 평가를 위해서도 사용 할 수 있도록 한다.


75 vs 25로 분할한다. 그리고 이미 임의로 섞여 있기 때문에 그냥 나누면 된다.


raw data / document-term matrix / corpus 이렇게 세가지 종류의 데이터 셋을 모두 분할 한다.

# creating training and test datasets
sms_raw_train <- sms_raw[1:4169, ]
sms_raw_test  <- sms_raw[4170:5559, ]

sms_dtm_train <- sms_dtm[1:4169, ]
sms_dtm_test  <- sms_dtm[4170:5559, ]

sms_corpus_train <- corpus_clean[1:4169]
sms_corpus_test  <- corpus_clean[4170:5559]

비율이 정상적으로 분할 되었는지는 아래의 명령어를 통해서 간단히 알 수 있다.

> # check that the proportion of spam is similar
> prop.table(table(sms_raw_train$type))
      ham      spam 
0.8647158 0.1352842 
> prop.table(table(sms_raw_test$type))
      ham      spam 
0.8683453 0.1316547 


■ Visualizing text data - word clouds

word cloud는 각 단어의 빈도를 text data로 묘사하게 된다.

여기서 보여지는 단어들의 배치는 랜덤이며 그 글자 크기가 빈도를 나타낸다.

이것을 통해서 경향을 파악할 수 있기 때문에 word cloud는 많이 사용 된다.


spam과 ham을 구분하는 예제에서는 word cloud를 그려 봄으로서 생성하려는 classifier가 성공적으로 동작 할지 안할지를

가늠 할 수 있게 된다.

word cloud는 Ian Fellows가 전문 통계학자로 UCLA에 있을때 만들어 진것이다.

http://cran.r-project.org/web/packages/wordcloud/index.html 에서 더 자세한 정보를 확인 할 수 있다.


install.packages("wordcloud")
library(wordcloud)
wordcloud(sms_corpus_train, min.freq = 30, random.order = FALSE)



random.order=FALSE 면, 높은 빈도일 수록 가운데에 집중 되는 경향을 보인다.

min.freq의 의미는 최소 빈도를 의미한다. 이것을 넘어야 cloud에서 보여지게 된다.

만약 word를 figure로 보지 못한다는 메시지를 본다면, min.freq를 조정 하면 된다.


이것을 spam과 ham에 대해서 각각 나눠서 수행 해 보자.

# subset the training data into spam and ham groups
spam <- subset(sms_raw_train, type == "spam")
ham  <- subset(sms_raw_train, type == "ham")
wordcloud(spam$text, max.words = 40, scale = c(3, 0.5))
wordcloud(ham$text, max.words = 40, scale = c(3, 0.5))



  


오른쪽이 스팸이고, 왼쪽이 Ham이다.

Spam의 경우 urgent, free, mobile, call, claim, stop 같은 것들이 있는것을 알 수 있다.

Ham의 경우 can, sorry, need, time이 나오는 것을 알 수 있다.



■ Data preparation - creating indicator features for frequent words

데이터 가공의 마지막 단계는 이러한 sparse matrix을 naive Bayes classifier가 처리할 수있는 데이터 타입으로 변형해 주는 것이다.

sparse matrix은 7000개의 feature들을 가지고 있다. 하지만 모든 feature들이 유용하지 않다는 것은 당연한 사실 이다.

따라서 이러한 feature들을 줄이는 작업을 수행 한다.


tm package에 있는 findFreqTerms() 함수를 이용 한다.

findFreqTerms(sms_dtm_train, 5)

위 command의 뜻은 최소 matrix으로 부터 최소 5번 이상 발생한 word로 구성된 character vector를 만들라는 명령어 이다.


이제 이러한 frequent word로만 구성 시켜서 matrix을 만들기 위해서 다시 DocumentTermMatrix을 수행 한다.

# indicator features for frequent words
sms_dict<- findFreqTerms(sms_dtm_train, 5)
sms_train <- DocumentTermMatrix(sms_corpus_train, list(dictionary = sms_dict))
sms_test  <- DocumentTermMatrix(sms_corpus_test, list(dictionary = sms_dict))

이렇게 수행 하면 최종적으로 7000개의 feature들은 1200개 수준으로 줄어 들게 된다.


naive Bayes classifier는 기본적으로 categorical feature들에 대해서만 학습을 수행 한다.

따라서 지금의 구조는 셀이 빈도를 나타내는 numerical type으로 표현 되므로 이것을 factor variable로 변경 해야 한다.

여기서는 "Yes", "No"와 같은 간단한 두 종류의 factor로 구분한다.

# convert counts to a factor
convert_counts <- function(x) {
  x <- ifelse(x > 0, 1, 0)
  x <- factor(x, levels = c(0, 1), labels = c("No", "Yes"))
}
# apply() convert_counts() to columns of train/test data
sms_train <- apply(sms_train, MARGIN = 2, convert_counts)
sms_test  <- apply(sms_test, MARGIN = 2, convert_counts)

apply를 이용해서 처리한다. margin=2의 의미는 column을 의미한다.

더 자세한 사항은 이전 포스트를 참조하자.




Step 3 - training a model on the data


이제 naive Bayes algorithm을 적용할 때가 왔다.

Machine Learning의 대부분의 시간은 지금과 같이 data 가공에 소모된다. 데이터만 가공된다면 적용하는것은 일도 아니다.

Naive Bayes algorithm의 원리는 이전 포스트의 "Probabilistic Learning - Classification using Naive Bayes

"를 참조 하자.


naive Bayes를 최적으로 구현하는 것은 쉬운 일이 아니므로, 이미 구현된 library를 사용하자.

"e1071" package에는 다양한 Machine Learning algorithm들이 구현되어 있다.

Vienna University of Technology (TU Wien)의 Statistics 학과에서 개발한 package이다.


install.packages("e1071")

library(e1071)


설치가 실패할 경우, 해당 package는 바이너리로 제공이 안되기 때문이다. 수작업으로 설치해 준다.

.libPaths() # 라이브러리 설치 경로 확인

https://cran.r-project.org/web/packages/e1071/index.html에서

Windows binaries항목의 zip파일을 다운 받은 다음 라이브러리가 저장된 폴더에 복사 한다.


사실 다른 package들도 많이 있다.

klaR 또한 naiveBayes를 지원 한다. 어느것을 쓰던 상관없으니 마음대로 찾아서 쓰면된다. 


Naive Bayes는 Learning과 Prediction을 구분해서 수행되는 알고리즘이다.

즉, eager learning 방식이다. 특별한 Model을 생성하고 prediction 하게 된다. 별도의 트레이닝 실행 시간을 필요로 하지만

prediction에 걸리는 시간은 트레이닝을 통해서 생성된 model에 기반하므로 Lazy Learning 방식 (KNN)에 비해서 빠르게 된다.


Building the classifier

m <- naiveBayes (train, class, laplace = 0)

  • train은 data frame 또는 matrix가 가능하다. training data를 포함하고 있다.
  • class는 factor vector 값이다. 각 행에서 class 값을 가지고 있는 것이다.
  • laplace 는 Laplace Estimator 사용을 위한 최소 값을 의미한다. Default 는 0이다.

해당 함수는 prediction을 위한 bayes model을 반환 한다.


Making Prediction

p <- predict (m, test, type = "class")

  • m은 model이다. naiveBayes()에 의해서 학습 되어진
  • test는 data frame 또는 matrix으로 test data set을 포함하고 있다. 당연히 training과 같은 형태와 수의 feature를 가지고 있어야 한다.
  • type은 "class" 또는 "raw"로 설정 할 수 있다. class는 가장 그럴것 같은 class로 예측해 주는것이고, raw는 확률을 반환해 주게 된다.

해당 함수는 type argument에 의해서 예측되어진 class 또는 확률의 vector를 반환 한다.

## Step 3: Training a model on the data ----
library(e1071)
sms_classifier <- naiveBayes(sms_train, sms_raw_train$type)
sms_classifier
$tables$yesterday
                  yesterday
sms_raw_train$type          No         Yes
              ham  0.995284327 0.004715673
              spam 0.996453901 0.003546099


데이터를 출력해서 보면 각각의 feature들에 대한 확률이 모두 계산되어진 것을 알 수 있다.

위와 같이 각 feature에 대한 조건 확률 들을 모두 계산해 두면 추후에 어떤 message에 대한 spam과 ham의 확률을 쉽게 구할 수 있다.

이것을 통해서 prediction을 즉각적으로 수행 하게 된다.




Step 4 - evaluating model performance 


테스팅을 위해서는 training에 사용하지 않는 message를 가지고 예측을 수행 하면 된다.


평가를 위해서는 이것저것 이전에 생성한 데이터 구조들을 사용 하게 된다.


sms_test 에는 보지 않은 데이터들의 feature들이 Yes와 No로써 저장되어 있다.

sms_raw_test에는 각 message가 "ham"인지 "spam"인지에 대한 참(true) 값이 저장 되어 있다.


학습을 통해서 생성한 classifier는 sms_classifier 이다. 그런 다음 생성한 prediction을 이용해서 true value와 비교하는 작업을 수행 한다.


prediction을 위해서 predict() 함수가 사용 되어 진다. 그리고 이러한 결과는 sms_test_pred vector에 저장 된다.

## Step 4: Evaluating model performance ----
sms_test_pred <- predict(sms_classifier, sms_test)

검증은 confusion matrix을 만들어서 수행 한다.

이를 위해서 CrossTable() 함수를 사용 한다.

> library(gmodels)
> CrossTable(sms_test_pred, sms_raw_test$type,
+            prop.chisq = FALSE, prop.t = FALSE, prop.r = FALSE,
+            dnn = c('predicted', 'actual'))

 
   Cell Contents
|-------------------------|
|                       N |
|           N / Col Total |
|-------------------------|

 
Total Observations in Table:  1390 

 
             | actual 
   predicted |       ham |      spam | Row Total | 
-------------|-----------|-----------|-----------|
         ham |      1202 |        31 |      1233 | 
             |     0.996 |     0.169 |           | 
-------------|-----------|-----------|-----------|
        spam |         5 |       152 |       157 | 
             |     0.004 |     0.831 |           | 
-------------|-----------|-----------|-----------|
Column Total |      1207 |       183 |      1390 | 
             |     0.868 |     0.132 |           | 
-------------|-----------|-----------|-----------|


위 confusion matrix을 통해서 괭장히 잘 prediction된 것을 알 수 있다.

오직 5개의 message만이 incorrectly 분류되었다 spam으로 이것은 전체 비중에서 0.4% 수준인 것이다.

31개 정도만이 그리고 spam인데 ham으로 prediction 되었다.

project에 투자한 노력에 비해서 상당히 우수한 prediction 실험 결과를 얻어낸 것을 알 수 있다.


하지만 문제는, 단 5개의 message만이 원래 ham인데 spam으로 분류 되었다 할지라도 이것은 심각한 문제를 야기할 수 있다.

즉 spam인데 ham으로 분류되는것은 단순히 불편함을 주지만, 중요한 메시지를 spam으로 분류해서 문제를 발생 시킨다면

그것이 99%의 정확도를 가질지라도 당장 해당 제품은 abandon 되어진다.

즉, False Negative 보다는 절대적으로 False Positive를 줄어야 하는 것이다.



Step 5 - improving model performance


오버피팅 문제를 야기할 수도 있다. 지금까지 생성한 모델은

왜냐하면 모델을 생성하기 위해서 사용한 트레이닝 데이터 셋에 "ringtone"이라는 feature가 오직 spam에서만 존재한다면

해당 경우에는 100% 확률로 spam으로 예측 된다.

의심할 여지 없이 문제가 있는 모델이다. 해당 트레이닝 데이터에서 최고의 정확도를 자랑하지만, 트레이닝 데이터가 현실의 모든 데이터를 반영하지 못하기 때문에 그것이 문제가 있다는 것은 직관적으로 알 수 있다.


laplace = 1로 설정해서 수행해 보자.

## Step 5: Improving model performance ----
sms_classifier2 <- naiveBayes(sms_train, sms_raw_train$type, laplace = 1)
sms_test_pred2 <- predict(sms_classifier2, sms_test)
CrossTable(sms_test_pred2, sms_raw_test$type,
           prop.chisq = FALSE, prop.t = FALSE, prop.r = FALSE,
           dnn = c('predicted', 'actual'))
   Cell Contents
|-------------------------|
|                       N |
|           N / Col Total |
|-------------------------|

 
Total Observations in Table:  1390 

 
             | actual 
   predicted |       ham |      spam | Row Total | 
-------------|-----------|-----------|-----------|
         ham |      1203 |        31 |      1234 | 
             |     0.997 |     0.169 |           | 
-------------|-----------|-----------|-----------|
        spam |         4 |       152 |       156 | 
             |     0.003 |     0.831 |           | 
-------------|-----------|-----------|-----------|
Column Total |      1207 |       183 |      1390 | 
             |     0.868 |     0.132 |           | 
-------------|-----------|-----------|-----------|


실험 결과 false positive가 5개에서 4개로 1개 줄어든 것을 알 수 있다.

비록 적은 향상이지만 이러한 false positive는 spam filtering product가 use 될지 abandon될지를 결정할 중요한 factor이므로 무시할 수는 없다.

그리고 여전히 false positive로 분류된 4개의 message에 대해서도 분석을 해봐야 할 것이다.








'AI > Machine Learning with R' 카테고리의 다른 글

Ensemble method: Bagging (bootstrap aggregating)  (0) 2015.11.19
Bootstrapping  (0) 2015.11.19
Principal Component Analysis (PCA)  (0) 2015.11.18
Evaluating Model Performance with R  (2) 2015.11.12
Naive Bayes with Caret package (R)  (0) 2015.11.12

통계처리 (기본)



Rounding of Numbers


정확한 계산을 필요로 하는 경우도 있지만, 때로는 반올림을 할 필요도 있다. 이 경우를 위해서 R은 아래와 같은 

몇가지 반올림 관련 함수들을 제공 한다.


ceiling(x)

floor(x)

trunc(x, ...)

round(x, digits =)

signif(x, digits =)


digits는 반올림할 decimal point의 위치를 결정 한다. 

상수면 소수점(decimal point)를 나타내며, 음수면 10,100의 자리를 나타낸다 (multiples of 10, 100).


round(123.456, digits=2)
[1] 123.46
round(-123.456, digits=-2)
[1] 100




만약 유효숫자(significant digits)를 숫자의 크기에 상관없이 명시하고 싶다면,

signif()를 사용할 수 있다.

> signif(-123.456,digits=4)
[1] -123.5
> signif(-123.456,digits=5)
[1] -123.46
> signif(-123.456,digits=1)
[1] -100
> signif(-123.456,digits=1)
[1] -100



round와 signif는 5를 기준으로 작은지 큰지에 따라 rounding을 수행 한다. 이와 다르게 한쪽 방향으로만 rounding을 수행하는

함수들도 존재 한다.


floor(x) 가장 작은 정수로 x가 결정 된다.

> floor(123.45)
[1] 123
> floor(-123.45)
[1] -124


ceiling(x) 가장 큰 정수로 x가 결정 된다.

> ceiling (123.45)
[1] 124
> ceiling (-123.45)
[1] -123



trunc(x) 가장 가까운 정수로 반올림되는데 방향이 0이랑 가장 근접한 쪽이다.

> trunc(123.45)
[1] 123
> trunc(-123.45)
[1] -123




데이터 분리 및 병합 (split, subset, merge)


split

split: 주어진 조건에 따라서 데이터를 분리 한다.
x # 분리할 벡터 또는 데이터 프레임
f # 분리할 기준을 지정한 factor

iris$Species에 따라서 분리하고 이것을 리스트에 저장 한다.

> head(split(iris, iris$Species))
$setosa
   Sepal.Length Sepal.Width Petal.Length Petal.Width Species
1           5.1         3.5          1.4         0.2  setosa
2           4.9         3.0          1.4         0.2  setosa

$versicolor
    Sepal.Length Sepal.Width Petal.Length Petal.Width    Species
51           7.0         3.2          4.7         1.4 versicolor
52           6.4         3.2          4.5         1.5 versicolor

$virginica
    Sepal.Length Sepal.Width Petal.Length Petal.Width   Species
101          6.3         3.3          6.0         2.5 virginica
102          5.8         2.7          5.1         1.9 virginica

split()의 실행 결과가 리스트므로, split()후 lapply()를 적용하면 iris의 종별 Sepal.Length의 평균을 구할 수 있다.

> lapply(split(iris$Sepal.Length, iris$Species),mean)
$setosa
[1] 5.006

$versicolor
[1] 5.936

$virginica
[1] 6.588

subset()

subset()
x # 일부를 취할 객체
subset # 데이터를 취할 것인지 여부

# 특정 조건에 의해서 데이터를 추출한다.
> subset(iris, Species == "setosa" & Sepal.Length > 5.0)
   Sepal.Length Sepal.Width Petal.Length Petal.Width Species
1           5.1         3.5          1.4         0.2  setosa
6           5.4         3.9          1.7         0.4  setosa
11          5.4         3.7          1.5         0.2  setosa
15          5.8         4.0          1.2         0.2  setosa
16          5.7         4.4          1.5         0.4  setosa
17          5.4         3.9          1.3         0.4  setosa
18          5.1         3.5          1.4         0.3  setosa
#특정 컬럼을 선택해서 데이터를 추출 한다.
> subset(iris, select=c(Sepal.Length, Species))
    Sepal.Length    Species
1            5.1     setosa
2            4.9     setosa
3            4.7     setosa
4            4.6     setosa
5            5.0     setosa
#특정 컬럼을 제거하는 두가지 서로 다른 스타일의 방법이다.
> subset(iris, select=-c(Sepal.Length, Species))
    Sepal.Width Petal.Length Petal.Width
1           3.5          1.4         0.2
2           3.0          1.4         0.2
3           3.2          1.3         0.2
4           3.1          1.5         0.2
5           3.6          1.4         0.2
6           3.9          1.7         0.4
7           3.4          1.4         0.3
> iris[, !names(iris) %in% c("Sepal.Length","Species")]
    Sepal.Width Petal.Length Petal.Width
1           3.5          1.4         0.2
2           3.0          1.4         0.2
3           3.2          1.3         0.2
4           3.1          1.5         0.2
5           3.6          1.4         0.2
6           3.9          1.7         0.4

merge

merge(): 두 데이터 프레임을 공통된 값을 기준으로 묶는 함수다.
Database에서 join과 같은 역할을 하는 함수 이다.

다음은 name column을 기준으로 수학 점수가 저장된 데이터 프레임과 영어 점수가 저장된 데이터 프레임을 병합한 예이다.
x와 y에 name값이 서로 다른 순서로 저장되어 있으나 공통된 name 값을 기준으로 점수가 잘 병합된 것을 볼 수 있다.

> x <- data.frame(name=c("a","b","c"), math=c(1,2,3))
> y <- data.frame(name=c("c","b","a"), english=c(4,5,6))
> merge(x,y)
  name math english
1    a    1       6
2    b    2       5
3    c    3       4

cbind의 경우 단순히 column만 합치는 것이기 때문에 merge()하고 그 결과가 다르다.

> cbind(x,y)
  name math name english
1    a    1    c       4
2    b    2    b       5
3    c    3    a       6

merge() 수행 시 공통된 값이 한쪽에만 있는 경우에는 반대편 데이터가 비게 되고 이 경우 해당 행은 병합 결과에서 빠진다.
NA를 추가해서라도 전체 데이터를 모두 병합하고 싶다면 all 인자에 TRUE를 지정 한다.

아래의 예제는 all=FALSE(기본값)인 경우와 all=TRUE인 경우의 비교를 보인 것이다.
all=FALSE인 경우에는 공통된 name이 양측에 포함된 "a","b"만 결과에 나타나며, all=TRUE인 경우 공통된 값이 없는 쪽에 NA가 채워지면서 x,y의 전체 행이 결과에 포함된다.

> y <- data.frame(name=c("c","b","d"), english=c(4,5,6))
> merge(x,y)
  name math english
1    b    2       5
2    c    3       4
> merge(x,y, all=TRUE)
  name math english
1    a    1      NA
2    b    2       5
3    c    3       4
4    d   NA       6


'AI > R Basic' 카테고리의 다른 글

JSON in R using jsonlite  (0) 2015.12.12
통계처리 (기본)  (0) 2015.10.28
doBy package, 데이터 그룹 처리  (0) 2015.10.22
R 기본 디버깅 (Debugging) 및 RStudio 이용  (0) 2015.10.04
R Studio 설치법 및 사용 Tip  (0) 2015.10.04

doBy package, 데이터 그룹 처리


doBy 패키지에는 summaryBy(), orderBy(), sampleBy()와 같은 특정 값에 따라 데이터를 처리하는 유용한 함수들이 있다.

다음은 이 함수들의 특징을 요약한 것이다.


 함수

특징 

 doBy::summaryBy()

 데이터 프레임을 컬럼 값에 따라 그룹으로 묶은 후 요약 값 계산 

 doBy::orderBy()

 지정된 컬럼 값에 따라 데이터 프레임을 정렬

 doBy::sampleBy()

 데이터 프레임을 특정 컬럼 값에 따라 그룹으로 묶은 후 각 그룹에 샘플 추출



install.packages("doBy")

library(doBy)



▣ summaryBy()


doBy::summaryBy: 포뮬러에 따라 데이터를 그룹으로 묶고 요약한 결과를 반환한다.

formula, #요약을 수행할 포뮬러

data=parent.frame() # 포뮬러를 적용할 데이터


반환 값은 데이터 프레임이다.


저것을 사용하기 위해서 formula 문법에 대해서 알아야 한다.



 연산자

예 

의미 

 Y~X1+X2

 Y를 X1, X2로 모델링, 상수항은 암시적으로 허용 된다. 따라서 선형 회귀에 이 포뮬러를 사용하면 Y=a*X1 + b * X2 + c 를 의미한다.

-

 Y~X1-X2

 Y를 X1로 모델링하되 X2는 제외한다.


특히, 선형 회귀에서 Y ~ X1 + X2 - 1은 Y를 X1과 X2로 모델링하되 상수항은 제외 한다는 의미다. 즉, Y= a*x1 + b * X2를 의미한다.

|

 Y~X1 | X2

 X2의 값에 따라 데이터를 그룹으로 묶은 후 각 그룹별로 Y~X1을 적용한다. 

:

Y ~ X1:X2 

 Y를 X1과 X2의 상호 작용 (interaction)에 따라 모델링 한다. 상호 작용은 Y = a * X1 * X2 + b 와 같이 X1과 X2가 동시에 Y 값에 영향을 주는 상황을 말한다.


특히, 영향을 주는 방식이 (+) 합 연산자의 예제인 Y= A*X1 +b*X2 +c와 같은 형태와는 전적으로 다르다.

*

 Y ~ X1 * X2

 Y ~ X1 + X2 + X1:X2의 충약형 표현이다.



summaryBy는 특정 조건에 따라 컬럼의 값을 요약해서 보여주게 된다.

아래의 경우 Sepal.Length와 Sepal.With를 Species에 따라 출력해서 보는 방식이다.

> summaryBy(Sepal.Width + Sepal.Length ~ Species, iris)
     Species Sepal.Width.mean Sepal.Length.mean
1     setosa            3.428             5.006
2 versicolor            2.770             5.936
3  virginica            2.974             6.588



▣ orderBy


doBy::orderBy: 포뮬러에 따라 데이터를 정렬한다.

formula, # 정렬할 기준을 지정한 포뮬러

# ~의 좌측은 무시하며, ~ 우측에 나열한 이름에 따라 데이터가 정렬된다.

data, # 정렬할 데이터


기본은 ascending 이기 때문에 descending으로 하고 싶을 경우 - (마이너)를 붙인다.

orderBy(~-Mean, dataFrame) 이런 식이다.


# 다음의 코드는 Species, Sepal.Width 순으로 정렬한 예이다.
> orderBy(~Species + Sepal.Width, iris)
    Sepal.Length Sepal.Width Petal.Length Petal.Width    Species
42           4.5         2.3          1.3         0.3     setosa
9            4.4         2.9          1.4         0.2     setosa
2            4.9         3.0          1.4         0.2     setosa
13           4.8         3.0          1.4         0.1     setosa
14           4.3         3.0          1.1         0.1     setosa


▣ sampleBy


doBy::sampleBy
formula, # ~우측에 나열한 이름에 따라 데이터가 그룹으로 묶인다.
frac = 0.1 #추출할 샘플의 비율이며 기본값은 10%
replace = FALSE # 복원 추출 여부
data=parent.frame() # 데이터를 추출할 데이터 프레임
systematic=FALSE # 계통 추출 (Systematic Sampling)을 사용할지 여부

반환 값은 데이터 프레임이다.


샘플 함수는 정보를 추출하는 함수이다. 아래와 같다.

10개 중 5개를 랜덤으로 추출한다. replace=TRUE를 설정하면 중복을 허용하는 것이다.

sampleBy과 대응 되므로 같이 알아두면 좋다.

> sample(1:10,5)
[1]  1 10  7  3  8
> sample(1:10,5, replace=TRUE)
[1] 8 8 2 3 6


이러한 샘플링은 주어진 데이터를 Training DataTest Data로 분리하는데 유용하게 사용 할 수 있다.


절적한 모델 테스팅을 위해선 균일한 분푸로 Test Data를 생성하는 것이 좋다. 이럴떄 sampleBy가 유용하게 사용 된다.

다음은 Iris 데이터에서 각 Species 별로 10%의 데이터를 추출한 예이다. sampleBy()에서 Species를 지정했으므로 각 종별로 5개씩 데이터가 정확히 샘플로 추출된 것을 볼 수 있다.

> sampleBy(~Species, fra=0.1, data=iris)
              Sepal.Length Sepal.Width Petal.Length Petal.Width    Species
setosa.13              4.8         3.0          1.4         0.1     setosa
setosa.24              5.1         3.3          1.7         0.5     setosa
setosa.27              5.0         3.4          1.6         0.4     setosa
setosa.35              4.9         3.1          1.5         0.2     setosa
setosa.40              5.1         3.4          1.5         0.2     setosa
versicolor.59          6.6         2.9          4.6         1.3 versicolor
versicolor.64          6.1         2.9          4.7         1.4 versicolor
versicolor.77          6.8         2.8          4.8         1.4 versicolor
versicolor.84          6.0         2.7          5.1         1.6 versicolor
versicolor.89          5.6         3.0          4.1         1.3 versicolor
virginica.102          5.8         2.7          5.1         1.9  virginica
virginica.104          6.3         2.9          5.6         1.8  virginica
virginica.121          6.9         3.2          5.7         2.3  virginica
virginica.132          7.9         3.8          6.4         2.0  virginica
virginica.144          6.8         3.2          5.9         2.3  virginica






R 기본 디버깅 (Debugging) 및 RStudio 이용


디버깅 방법은 아래와 같이 크게 2가지로 나눠 진다.

  • print(), sprint(), cat()을 사용해서 메시지나 객체의 내용을 출력해보는 방법
  • browser()를 사용한 코드 디버깅 방법


print()


print()문으로 중간 중간 데이터를 찍어 보면서 수행 하도록 한다.

paste() 함수와 같이 쓰는면 유용하다.

인자간의 공백을 사용하지 않는 함수는 paste0() 함수 이다.

fibo <- function (n) {
    if (n == 1 || n == 2) {
        print (" base case ")
        return (1)
    }
    print ( paste0 (" fibo (", n - 1, ") + fibo (", n - 2, ")"))
    return ( fibo (n - 1) + fibo (n - 2))
}
> fibo(3)
[1] " fibo (2) + fibo (1)"
[1] " base case "
[1] " base case "
[1] 2


sprintf()


print()와 유사하지만 주어진 인자들을 특정한 규직에 맞게 문자열로 변환해 출력 한다.

sprintf("포멧팅 문자열",인자1,인자2,..인자n)
# %d / %f / %s (C언어의 문법과 유사하다)


cat()


행 바꿈을 수행하지 않고 출력하는 특징이 있다.

줄바꿈 하고 싶으면 "\n"을 수행 하면 된다.


이러한 특징 때문에 cat()을 사용해서 테스트 방식의 프로그레스 상태 메시지를 보여줄 수 있다.

> sum_to_ten <- function () {
+     sum <- 0
+     cat(" Adding ...")
+     for (i in 1:10) {
+         sum <- sum + i
+         cat(i, "...")
+     }
+     cat(" Done !", "\n")
+     return (sum)
+ }
> sum_to_ten()
 Adding ...1 ...2 ...3 ...4 ...5 ...6 ...7 ...8 ...9 ...10 ... Done ! 
[1] 55


Cat을 이용한 또다른 디버깅 코드이다.

#for detecting error, analyze each Corpus

sapply(1:length(vTitle), function(x){

    cat(x,'...')

    sms_corpus_clean <- tm_map(sms_corpus[x], content_transformer(tolower))

    if(x%%10 == 0){

        cat('\n')

    }

})



browser()


해당 함수가 호출 되면 명령어의 수행이 중지되고, 해당 시점부터 디버깅 모드가 시작된다.

원하는 부분에다가 직접 browser() 코드를 삽입 해야한다.

그럼 해당 코드 위치에 도달 했을때

Browse[1]> 이라는 명련 표시줄로 변경이 된다.

명령어는 gdb와 대부부 같다.

n,s,c

종료는 Q 이다.


browser()만의 장점은 다음과 같다.

  • 어떠한 editor에서도 사용하다.
  • 추가적으로 conditional breakpoint의 기능을 구현할수도 있다.
for (i in 1:1024) {
  start_work()
  if (i == 512)
    browser()
  finish_work()
}



R-Studio를 이용한 디버깅 방법



아래와 같이 편집기에서 마우스로 클릭하면 breakpoint를 설정 할 수 있다.

해당 라인에서 Shift + F9를 해도 된다.


breakpoint를 설정하면 아래와 같이 걸려있는 함수를 환경변수 패널에서 확인 할 수 있다.


만약 아래와 같은 경고가 나온다면 이유는 두가지 이다.

첫 번째는 source()를 통해서 해당 파일을 읽어 들이지 않아서 그래서 object가 생성되지 않았을 때이다.

두 번째는 editor 상의 file과 source()로 읽어들인 file간의 차이가 존재 했을 때이다.


위 두상황 모두 source()를 해주면 해결 된다.



정상적으로 breakpoint를 걸었다면,

이제 해당 코드를 실행하면 원하는 위치에서 멈추는거을 알 수 있다.

R Studio는 디버깅 명령어들을 모두 위즈윅 모드로 제공하므로 마우스를 써서 디버깅 하면 된다.


디버깅 실행 화면



만약 source 코드가 없는 상태에서 디버깅 하고 싶다면?


install.packages("devtools")


참고문헌: Debugging with RStudio





R Studio 설치법 및 사용 Tip



R Studio의 특징


대화식 R

R 그래픽 작업

코드 구조화, 다수의 프로젝트 관리

분석 내용 재현

R에 설치된 패키지 관리

분석 보고서 작성 및 공유

본인의 코드 공유 및 다른 사용자와의 협업

다양한 적용 가능 플랫폼

윈도우

리눅스

맥 OS X



Comprehensive R Archive Network


R언어를 일단 잘알아야 RStudio를 통해서 통계분석이 가능하다.

위즈윅(WYSIWYG)에 익숙한 사람들, 즉 마우스로 클릭하고 데이터를 이동하는것에 익숙 했던 사람들은 여간 사용하기 어려운것이 아니다.



하지만, 분석의 재현을 위해서는 반드시 각 분석 단계를 소스코드 형태로 저장해야한다.

그래야 분석 과정을 재현하기 쉽다.


프로그래밍 방식은 또한 

단순히 사전식으로 해당 기능에대해서만 동작하는 위즈윅 방식의 툴보다 훨씬 유연하며 강력한 기능을 조합을통해서 달성 할 수 있다.



항상 최신 버전을 사용하라

View(news()) 를통해서 최신 정보를 알 수 있다.

update.packages()를 통해서 페키지를 업데이트 하라



설치방법


1. R을 설치해야한다.


Rstudio는 기본적으로 2.11 이상의 R을 설치해야한다. 

공식 사이트: http://www.r-project.org/

이곳에서 가장 가까운 서버인

http://cran.nexr.com/

위 사이트에서 Window용 R을 다운 받았다.

Tip: 항상 최신 버전의 알을 수시로 확인해서 다운받아서 설치하자


1.1 업데이트 방법

기본적으로 자동 업데이트는 제공하고 있지 않으므로, 이전 버전을 제거한다음 새로 설치하면 된다.


이전 package 설치본들을 복사한 다음 붙여 넣기 한다.

그 다음 현재 package들을 업데이트 한다.

update.packages(checkBuilt=TRUE, ask=FALSE) 


개인 라이브러리의 경우 아래의 경로에 저장 되어 있다.

C:\Users\<사용자이름>\Documents\R\win-library\3.3


현재 R에 설치된 라이브러리 리스트를 얻어서 그대로 재설치하는 방법도 있다.

library()


현재 R의 버전을 알아내는 방법


package_version(R.version)


아래의 명령어를 통해서 설치된 package와 설치된 version을 알 수 있다.


installed.packages()[,c("Package","Version")]
> installed.packages()[,c("Package","Version")]
             Package        Version   
BH           "BH"           "1.58.0-1"
bitops       "bitops"       "1.0-6"   
brew         "brew"         "1.0-6"   
caTools      "caTools"      "1.17.1"  
colorspace   "colorspace"   "1.2-6"   
curl         "curl"         "0.9.3"   



2. R studio 설치

공식 사이트: http://www.rstudio.com/products/rstudio/download/

Tip: RStudio에서

Help > Check for Updates를 클릭하여 신규 업데이트 정보를 확인하자.

업데이트가 필요하면 위 공식 사이트에가서 신규버전으로 다시 설치하자.

아직까지는 자동 업데이트 기능은 없는것 같다.


3. 필수 package 설치 및 로드

R studio의 하단에 보면 package tab이 있다. 이곳에서 이미 설치된 package들의 리스트를 확인 할 수 있다. 


update 버튼을 통해서 기존 package들을 업데이트 할 수도 있다. 


그리고 install을 통해서 새로운 package들을 쉽게 설치 할 수 있다. 


추가로 설치한 리스트

install.packages("ggplot2")

xlsx 

knitr // A General-Purpose Package for Dynamic Report Generation in R


설치된 package를 로드하기위해서는

library 명령어를 타이핑 해도되고

package 창에서 마우스로 check 해도 된다.



단축키 (shortcuts)


Keyboard Shortcuts: R support





GIt을 이용한 버전 관리


git을 설치한다.

자신의 운영체제에 맞춰서 설치파일을 다운로드 한다.

https://git-scm.com/downloads



Next > Next 만을 선택해서 설치한다.

설치확인 방법은 아래와 같이 적당한 디렉터리 아무곳에서나 오른쪽 마우스 클릭을해서

Git bash here 와 같은 명령어들이 모이면 정상적으로 설치된 것이다.




RStudio와 연동하기

메뉴 바에서

Toos -> Global Options -> Git/SVN 에서 Git을 설치한 디렉터리에서 git 실행파일을 연결해 준다.


git



이렇게 하고 재부팅을 해준다.

아래와 같이 정상적으로 git tab이 생성되는것을 알 수 있다.





자료형(integer,character) 변환 및 데이터 구조(dataFrame, list) 변환



자료형


각각의 자료형들간의 변환은 아래의 함수들을 이용 한다.

as.character(x)

as.complex(x)

as.numeric(x) or as.double(x)

as.integer(x)

as.logical(x)





R에서는 데이터 분석을 위한 유용한 데이터 구조를 기본으로 제공해 준다.

데이터 구조(5개): vector, list, data frame, matrix(array), factor 


모드: 

R에서 해당 객체를 메모리에 어떻게 저장할 것인가를 가리키는 것이 "모드"이다

숫자로 저장할 것인가?

문자열 ?

다른 객체로의 포인터로된 리스트들?

함수?

위의 다양한 것들로서 메모리에 저장 될 수 있다.


객체, 예, 모드

숫자, 3.21415, 수치형

숫자백터, c(2, 3), 수치형

문자열, "More", 문자형

문자열 백터, c("More","Larry","Curly"), 문자형

요인, factors(c("NY","CA","IL")),수치형

리스트, list("More","Larry","Curly"), 리스트

데이터 프레임, data.frame(x=1:3, y=c("NY","CA","IL")), 리스트

함수, print, 함수


알아내느 방법

mode()



Class: 추상적인 자료형


R에서는 모든 객체는 추상 자료형인 '클래스'도 가지고 있다.

거리, 시간, 무게 모두 어떤 숫자이므로 모드는 숫자(numeric)이다.

하지만 각각의 해석 방법이 다르므로 클래스는 상이할 수 있다.


결국 클래스가 R에서 직접적인 데이터 처리 방식을 결정하게 된다.


예를들면 print()라는 함수는 입력된 데이터의 class에 따라서 그 처리를 다르게 하는 것이다.

위에서 설명한 모드는 메모리에 저장되는 형태지, 논리적으로 프로그래밍 할 때 쓰는 단위는 아니다.





Factor (범주형 자료)



생성 방법

factor(

x, #팩터로 표현하고자 하는 값(주로 문자열 벡터로 지정)

levels, #값의 레벨

ordered #TRUE면 순서형, FALSE면 명목형 데이터를 뜻한다. 기본값은 FALSE다.

)


팩터에서 레벨의 개수를 반환함.

nlevels(

x # 팩터 값

)


중간에 값 넣기

factor(append(as.character(proximity),"TRUE"))


factor level rename

http://www.cookbook-r.com/Manipulating_data/Renaming_levels_of_a_factor/



Vectors


백터는 동질적이다.

같은 자료형을 가진다.

같은 모드를 가지고 있어야 한다.


스칼라와 백터의 관계는 통상 엄밀히 구분하지만 R에서는 딱히 구분하지 않는다.

하나의 값으로 구성된 백터가 스칼라고

여러개 있으면 백터라고 생각하면 된다.




List


이질적이다.

다양한 자료형을 포함 할수 있다.


생성방법

list <- list(x,y,z) #이질적인 데이터도 가능함.

list <- list(mid=0.5, right=0.841, far=0.9)



접근방법

리스트 변수[[인덱스] # 값을 접근함

리스트 변수[인덱스] # (키,값) 의 형태로 서브 리스트를 반환 한다.


제거방법

x[-2] # without 2nd element

x[-c(2,3)] # without 2nd and 3rd

x["arrival_time"] <- NULL


빈리스트를 생성하고 인덱스를 이름으로 지정하는 방법

vecNames <- list.files("./ESLABCollector/")
filenames <- sapply(vecNames,list)  

Foreach를 사용할 때 list 인덱스의 이름을 유지하는 방법

jsonDfList <- foreach (x = rawDataList, .final = function(x) setNames(x, names(filenames))) %do% {

    jsonTodf(x)

}


.final 을 이용한다.

결국 setNames란 아래의 것을 축약한 기능이다.

setNames( 1:3, c("foo", "bar", "baz") )

# this is just a short form of

tmp <- 1:3

names(tmp) <-  c("foo", "bar", "baz")

tmp



List 중간에 값을 삽입 하기


append(testList, list(x=42), 3)

$`1`

[1] 1

$`2`

[1] 2

$`3`

[1] 3

$x

[1] 42

$`4`

[1] 4


append(lst, list(f=1:5), after=0)# after - position to append

Helper 함수를 생성하는 방법

lappend <- function (lst, ...){
lst <- c(lst, list(...))
  return(lst)
}

> a <- list()
> a
list()

> lappend(a,c(1,2,3))
[[1]]
[1] 1 2 3

> lappend(a, c(4,5,6), c(7,8,9))
[[1]]
[1] 4 5 6

[[2]]
[1] 7 8 9




Matrix


백터에 차원을 정해주면 바로 Matrix로 변경 된다.


백터의 dim 속성이 처음엔 NULL 이지만 이것을 주게 되면 matrix가 된다.

dim(2,3) # 2 by 3 행렬


list도 dim 변경을해서 행렬화 할 수 있다.

하지만 list의 특성상 이질적인 행렬을 만들어 낼 수 있으므로 그러한 것들은 조심해야 한다.




Array (배열)


3차원 matrix 부터는 배열이라는 이름을 쓴다.


Factor(요인)

백터처럼 생겼지만 특별한 속성이 있다.

R은 백터에 있는 고유한 값(unique value)의 정보를 얻어 내는데, 이 고유 값들을 요인의 '수준(level)'이라고 일컫는다.

요인들은 간단하고 효율적인 형태로 데이터 프레임에 저장된다.

다른 프로그래밍 언어에서는 요인을 '열거형 값(enumerated value)들로 이루어진 백터로 표현한다.


요인의 사용처는 아래와 같다.


범주형 변수>

요인 하나는 범주형 변수 하나를 나타낼 수 있다. 

범주형 변수들은 분할표, 선형 회귀, 변량 분석, 로지스틱 회귀 분석 등 많은 분야에서 사용 된다.


집단 분류

이것은 데이터 항목에다가 집다에 따른 라벨을 붙이거나 태깅을 할 때 쓰는 방법이다.




데이터 프레임 (Data Frame)


가장 중요한 데이터 타입이다.

표 형태의 데이터를 표현하기 위함이다.


특징

행은 모두 같은 길이이다. 열은 하나의 엘리먼트를 나타낸다.

행렬과 다른점은 각각의 행들이 서로다른 데이터 타입들을 가질 수 있다는 것이다.

즉, 각각의 열들은 같은 타입으로 이뤄지지만, 모든 열이 모두 같을 필요는 없다.

데이터 프레임의 속성은 row.names()를 통해서 알 수 있다.

통상 read.table() 또는 read.csv()를 통해서 생성 한다.

행렬을 data frame으로 변경 할 수 있다. data.matrix() 호출을 통해서



▣ 데이터 프레임 생성하기

data.frame()을 사용


g <- c("A","B")

x <- 1:3

dat <- data.frame(g,x)


▣ 리스트를 프레임 데이터로 변경

frame.data(list)



▣ 프레임의 구조를 알려준다.

str(data)

> str(ac)

'data.frame': 356 obs. of  2 variables:

 $ Time : num  0 0.2 0.4 0.6 0.8 1 1.2 1.4 1.6 1.8 ...

 $ Power: num  14.8 14.1 19.9 14.6 14.1 ...



▣ 프레임에서 열들만 따로 출력하는 방법이다.

data$time



▣ 데이터 프레임의 Index를 알아내는 방법

> a <- data.frame(1:6,11:16)

> a

  X1.6 X11.16

1    1     11

2    2     12

3    3     13

4    4     14

5    5     15

6    6     16

> grep(15,a[,2])

[1] 5


▣ 해당 컬럼 이름이 몇번째 인지를 알아내는 방법

> grep("X11.16",colnames(a))

[1] 2



▣ 프레임에서 행과 열만 따로 출력하는 방법이다.

data$time[0]



▣ 위치로 데이터 프레임의 열 선택하기


스타일에 따라 또는 데이터프레임 두가지 자료형중 하나로 반환 된다.


데이터 프레임 스타일의 선택 방법

dfm[[n]] # 하나의 열을 반환 한다.

dfm[n] # n번째 열 단독으로만 구성된 '데이터 프레임'을 반환 한다.

dfm[c(n1,n2,...,nk)] # n1,n2,....,nk 위치에 있는 열들로 만든 "데이터 프레임"을 반환한다.


행렬 방식의 첨자 지정을 이용해서 선택 하는 방법

dfm[,n] # n번째 을 반환 한다

dfm[,c(n1,n2,...,nk)] # n1,n2,...,nk 위치에 있는 열들로 만든 "데이터 프레임"을 반환 한다.



▣ 프레임 열의 이름을 변경하는 방법이다.

names(data) <- c("name1", "name2", "name3")

 


▣ 특정 열을 찾아서 이름을 변경하는 방법

> names(ac)[names(ac)=="Time"] <- c("newTime")

> names(ac)

[1] "newTime" "Power"  

> names(ac)[names(ac)=="newTime"] <- c("Time")

> names(ac)

[1] "Time"  "Power"



▣ 데이터 프레임에 열 추가하기

새로운 열의 이름을 설정하고 "NA"로 채운다.

data$newcol <- NA


새로운 열의 이름을 설정하고 백터를 삽입한다.

data$newcol <- vec



▣ 데이터 프레임에 열을 제거하기

해당열에 NULL을 대입한다.


data$badcol <- NULL

subset(data, select = -badcol)

subset(data, select = c(-badcol, -othercol)



▣ 데이터 프레임 열 순서 변경하기

#숫자로 해당열을 선택해서 변경한다.

dat <- dat[c(1,3,2)]


#이름으로 선택해서 변경 한다.

data <- dat[c("col1","col3","col2")]



▣ 데이터 프레임의 부분 집합 취하기

subset() 함수를 이용해서 부분 집합을 취할 수 있다.


일련의 조건을 기입하고 그 조건에 맞는 행등을 추출 할 수 있다.


subset(climate, Source == "Berkeley", select = c(Year, Anomaly10y))

# 데이터 셋 climate에서

# Source 열이 "Berkeley인 것에 대해서

# Year 와 Anomaly10y의 열에 대해서만 그 값을 추출 


논리 연산자를 조합하면 일정 범위 내의 데이터를 추출 할 수도 있다.


1900년에서 2000년 사이의 source가 "Berkeley"인 행들만 선택 한다.

subset(climate, Source == "Berkeley" & Year >= 1900 & Year <= 2000, select = c(Year, Anomaly10y))



▣ 여러가지 방법을 이용한 데이터 프레임 합치기


데이터 프레임 합치기

cbind # 두개의 데이터를 열로 쌓는다.

rbind # 두개의 데이터를 행으로 쌓는다.


연속적인 리스트라면,

do.call(rbind,mylist)를 하면된다.


주의: 위방법들은 모두 행열이 같은 크기일때만 가능함

크기가 서로 다르면 반복해서 아랫부분을 채워준다고 하지만 이상하게 동작하지 않는다.


서로다른 데이터 프레임 합치기

merge # SQL에서 join에 해당하는 명령어이다

default로 동작은 그냥 다순히 합치는 것이다.

일치하지 않는것이 있다면 데이터의양이 NxM의 양으로 늘어 난다.


일치하는 부분만 합치고 싶으면,

merge(A,B, by="열 이름") 

으로 설정한다.


merge의 다른 option들

## S3 method for class 'data.frame'
merge (x, y, by = intersect(names(x), names(y)),
      by.x = by, by.y = by, all = FALSE, all.x = all, all.y = all,
      sort = TRUE, suffixes = c(".x",".y"),
      incomparables = NULL, ...)

x, y

data frames, or objects to be coerced to one.

by, by.x, by.y

해당 기준으로 합친다. 없는것는들은 제거한다.

즉, 가장 짧은 기준으로 합친다.

5, 10 이라면 5만큼 길이가 설정됨.

all

logical; all = L is shorthand for all.x = L and all.y = L, where L is either TRUE or FALSE.

all.x

logical; if TRUE, then extra rows will be added to the output, one for each row in x that has no matching row in y. These rows will have NAs in those columns that are usually filled with values from y. The default is FALSE, so that only rows with data from both x and y are included in the output.

all.y

logical; analogous to all.x.

sort

logical. Should the result be sorted on the by columns?

suffixes

a character vector of length 2 specifying the suffixes to be used for making unique the names of columns in the result which not used for merging (appearing in by etc).

incomparables

values which cannot be matched. See match. This is intended to be used for merging on one column, so these are incomparable values of that column.


서로다른 크기의 데이터 프레임 합치기

일치하지 않는 컬럼을 단순히 <NA>로 채우고 싶다면,

plyr package의 rbind.fill을 사용하면 된다.



향상된 데이터 프레임 합치기

reshape2 package

본인 정리

Reshape and aggregate data with the R package reshape2


plyr package

본인정리


데이터 프레임 행과 열 크기 알아내기

nrow()

ncol()


help(package = "reshape2")

원본 참고 사이트: http://seananderson.ca/2013/10/19/reshape.html

reshape 2 website: http://had.co.nz/reshape/



서로 다른 데이터 타입들간의 데이터 상호 변환




변환 

방법 

주석 

 벡터 -> 리스트

as.list(vec) 

 

 벡터 -> 행렬

cbind(), as.matrix()

rbind(), matrix(vec,n,m) # n by m 행렬 

생성 

 

 벡터 ->데이터 프레임

#열 한 개짜리 데이터 프레임

as.data.frame(vec)

# 행 한 개짜리 데이터 프레임

as.data.frame(rbind(vec))

 

 리스트 -> 벡터

unlist(list)

as.vector 대신에 unlist가 좋다. 

 리스트 -> 행렬 

열 한 개짜리 행렬: as.matrix(list)

행 한 개짜리 행렬: as.matrix(rbind(list))

n by m 행렬: matrix(list,n,m) 

 

 리스트 -> 데이터 프레임

목록 원소들이 데이터의 열이면:

as.data.frame(list)

 

 

 행렬 -> 벡터

 as.vector(mat) 

 행렬의 모든 원소들을 벡터로 반환 한다. 

 행렬 -> 리스트

 as.list(mat)

 행렬의 모든 원소들을 리스트로 변환 한다.

 행렬 -> 데이터 프레임

 as.data.frame(mat) 

  

 데이터 프레임 -> 벡터

 dfm[1,]#행 하나 짜리 데이터 프레임 변환

 dfm[,1] or dfm[[1]] #열 하나짜리 데이터 프레임 변환

 

 데이터 프레임 -> 리스트

 as.list(dfm)

 

 데이터 프레임 -> 행렬

 as.matrix(dfm)

 






'AI > R Basic' 카테고리의 다른 글

R 기본 디버깅 (Debugging) 및 RStudio 이용  (0) 2015.10.04
R Studio 설치법 및 사용 Tip  (0) 2015.10.04
Apply 함수 (데이터 조작)  (1) 2015.10.03
R의 문자열 처리 및 비교  (0) 2015.10.03
Plyr package (데이터 조작)  (0) 2015.10.01

R에서는 주로 벡터 기반으로 데이터를 처리 한다.

벡터기반 처리는 개별 요소를 for 루프 등으로 하나씩 처리하는 방식보다 빠르게 수행될 뿐만 아니라 손쉽게 병렬화가 가능하다.

따라서 대부분의 Machine Learning이나 통계 함수들은 입력값으로 벡터를 받는다.


따라서 데이터를 벡터로 변환하고 다시 벡터데이터를 가독성 높은 데이터 프레임으로 변경하는 것을 자유 자재로 할 수 있어야한다.

앞으로 보게될 많은 R 코드들은 모두 이러한 벡터 연산들을 이용하기 때문에 벡터를 다루는 함수들을 잘 알고 있어야 한다.



Apply 함수 (데이터 조작)


벡터화된 연산과 apply함수를 이용해서 일괄 실행함으로써 계산을 간소화하고 고속화를 도모 한다.

다양한 데이터를 임의의 함수에 적용하 결과를 얻는 방법으로 apply  함수들을 이용하는 방법이 있다.

이들 함수는 벡터, 행렬, 리스트, 데이터 프레임에 적용할 함수를 명시하는 형태로서, 함수형 언어 스타일에 가깝다고 볼 수 있다.



apply 함수들의 요약


apply() 

배열 또는 행렬에 주어진 함수를 적용한 뒤 그 결과를 벡터, 배열 또는 리스트로 반환

배열 또는 행렬에 적용 가능


lapply()

벡터, 리스트 또는 표현식에 함수를 적용하여 그 결과를 리스트로 반환

결과가 리스트


sapply() 

lappy()와 유사하지만 결과를 벡터, 행렬 또는 배열로 반환

결과가 벡터, 행렬 또는 배열


tapply()

벡터에 있는 데이터를 특정 기준에 따라 그룹으로 묶은 뒤 각 그룹맏 주어진 함수를 적용하고 그 결과를 반환

여러 데이터를 함수의 인자로 적용


mapply() 

sapply의 확장된 버전으로, 여러 개의 벡터 또는 리스트를 인자로 받아 함수에 각 데이터의 첫째 요소들을 적용한 결과,

둘째 요소들을 적용한 결과, 셋째 요소들을 적용한 결과 등을 반환

여러 데이터를 함수의 인자로 적용 가능



▣ Apply 사용법


apply: 배열 또는 행렬에 함수 f를 d방향으로 적용하여 결과를 벡터, 배열 또는 리스트로 반환 한다.

apply( X, d, f)

X #배열 또는 행렬

d # 함수를 적용하는 방향, 1은 행방향, 2는 열 방향

  # c(1,2)는 행과 열 방향 모두를 의미

FUN # 적용할 함수


예제

#합 구하는 함수
> sum(1:10)
[1] 55
# 메트릭스 생성
> d <- matrix(1:9, ncol=3)
> d
     [,1] [,2] [,3]
[1,]    1    4    7
[2,]    2    5    8
[3,]    3    6    9
# 각각의 행을 더해서 벡터로 반환 한다.
> apply(d,1,sum)
[1] 12 15 18
# 각각의 열을 더해서 벡터로 반환 하다.
> apply(d,2,sum) 
[1]  6 15 24


> head(iris[c(1,2,3,4)])
  Sepal.Length Sepal.Width Petal.Length Petal.Width
1          5.1         3.5          1.4         0.2
2          4.9         3.0          1.4         0.2
3          4.7         3.2          1.3         0.2
4          4.6         3.1          1.5         0.2
5          5.0         3.6          1.4         0.2
6          5.4         3.9          1.7         0.4
# 벡터 스타일의 선택
> apply(iris[c(1,2,3,4)],2,sum)
Sepal.Length  Sepal.Width Petal.Length  Petal.Width 
       876.5        458.6        563.7        179.9 
# 메트릭스 스타일의 선택
> apply(iris[,1:4],2,sum)
Sepal.Length  Sepal.Width Petal.Length  Petal.Width 
       876.5        458.6        563.7        179.9




▣ lapply 사용법


리스트로 반환하는 특징이 있는 apply 계열 함수이다.


lapply (X, FUN, ...#추가인자) 

반환 값은 X와 같은 길이의 리스트이다.

X는 벡터, 리스트, 표현식, 데이터 프레임을 받을 수 있다.


리스트를 다른 타입으로 변환 하는 방법 

unlist( x, recursive=FALSE, use.name=TRUE )

반환 값은 벡터이다.


do.call # 함수를 리스트로 주어진 인자에 적용하여 결과를 반환한다.

(적용할 함수, 데이터 리스트)

반환 값은 함수 호출 결과다.



# 벡터 입력을 받아서 리스트로 반환하는 코드이다.
> result <- lapply(1:3, function(x){x*2})
> result
[[1]]
[1] 2

[[2]]
[1] 4

[[3]]
[1] 6

> result[[1]]
> unlist(result) # list 구조를  벡터로 변경 한다.
[1] 2 4 6


입력을 list로 받을 수도 있다.

> x <- list(a=1:3, b=4:6)
> x
$a
[1] 1 2 3

$b
[1] 4 5 6

> lapply(x,mean)
$a
[1] 2

$b
[1] 5


# iris의 데이터 구조를 보여줌
> head(iris)
  Sepal.Length Sepal.Width Petal.Length Petal.Width Species
1          5.1         3.5          1.4         0.2  setosa
2          4.9         3.0          1.4         0.2  setosa
3          4.7         3.2          1.3         0.2  setosa
4          4.6         3.1          1.5         0.2  setosa
5          5.0         3.6          1.4         0.2  setosa
6          5.4         3.9          1.7         0.4  setosa

# list로 반환됨 각각의 열들의 평균을 구한다음
> lapply(iris[,1:4],mean)
$Sepal.Length
[1] 5.843333

$Sepal.Width
[1] 3.057333

$Petal.Length
[1] 3.758

$Petal.Width
[1] 1.199333

#각 열에 대한 평균을 구하게 된다.
> colMeans(iris[,1:4])
Sepal.Length  Sepal.Width Petal.Length  Petal.Width 
    5.843333     3.057333     3.758000     1.199333


데이터 프레임을 처리한 결과를 리스트로 얻은 뒤 해당 리스트를 다시 데이터 프레임으로 변활할 필요가 있다. 

이 변환은 몇 단계를 거쳐서 처리해야 한다.

  • unlist()를 통해 리스트를 벡터로 변환한다.
  • matrix()를 사용해 벡터를 행렬로 변환한다.
  • as.data.frame()을 사용해 행렬을 데이터 프레임으로 변환한다.
  • names()를 사용해 리스트로부터 변수명을 얻어와 데이터 프레임의 각 컬럼에 이름을 부여한다.


iris 데이터의 각 열의 평균을 구한다음

이 결과는 list로 반환 되므로 이것을 다시 데이터 프레임으로 변경해서 출력하는 예제이다.

># 데이터 프레임으로 변겨하는 코드이다.
# matrix()로 먼저 변환 하는 것은 vector를 바로 data frame으로 변경하면 단일 열로 밖에 변경을 못하기 때문이다.
 d <- as.data.frame(matrix(unlist(lapply(iris[,1:4],mean)),ncol=4,byrow=TRUE))
> d
        V1       V2    V3       V4
1 5.843333 3.057333 3.758 1.199333
> d <- as.data.frame(matrix(unlist(lapply(iris[,1:4],mean)),ncol=4,byrow=TRUE))
> d
        V1       V2    V3       V4
1 5.843333 3.057333 3.758 1.199333
#위와 같이 이름이 자동으로 설정되므로 가독성을 위해서 이름을 변경해 준다.
> names(d) <- names(iris[,1:4])
> d
  Sepal.Length Sepal.Width Petal.Length Petal.Width
1     5.843333    3.057333        3.758    1.199333



unlist 방식의 경우 벡터로 바꾸기 때문에 데이터 타입이 서로 다를 경우 엉뚱한 값으로 바꿔버리게 된다.

> x <- list(data.frame(name="foo",value=1),data.frame(name="bar",value=2))
> x
[[1]]
  name value
1  foo     1

[[2]]
  name value
1  bar     2

> unlist(x)
 name value  name value 
    1     1     1     2 

이렇게 서로 다른 데이터로 구성된 list를 데이터 프레임으로 변경 해야한다면,

do.call() 함수를 이용 해야 한다.

> do.call(rbind,x)
  name value
1  foo     1
2  bar     2

하지만, 해당 방식은 속도면에서 문제가 발생 한다.

그래서 rbindlist()를 사용하게 된다.




▣ sapply 사용법


lapply의 사용자 친화 버전이다. 해당 함수는 리스트 대신에 
행렬과 벡터 등의 데이터 타입으로 결과를 반환하는 특징이 있는 함수 이다.

벡터 리스트 표현식 데이터 프레임등에 함수를 적용하고 그 결과를 벡터 또는 행렬로 반환한다.
sapply {
X, # 벡터, 리스트, 표현식 또는 데이터 프레임
FUN, # 적용할 함수
....., # 추가 인자, 이 인자들은 FUN에 전달된다.
}

> sapply(iris[,1:4],mean)
Sepal.Length  Sepal.Width Petal.Length  Petal.Width 
    5.843333     3.057333     3.758000     1.199333 
> class(sapply(iris[,1:4],mean))
[1] "numeric"


sapply로 연산하고 벡터를 data frame으로 변경하는 방법이다.

> sapply(iris[,1:4],mean)
Sepal.Length  Sepal.Width Petal.Length  Petal.Width 
    5.843333     3.057333     3.758000     1.199333 
> class(sapply(iris[,1:4],mean))
[1] "numeric"
> x <- sapply(iris[,1:4],mean)
> as.data.frame(x)
                    x
Sepal.Length 5.843333
Sepal.Width  3.057333
Petal.Length 3.758000
Petal.Width  1.199333
> as.data.frame(t(x))
  Sepal.Length Sepal.Width Petal.Length Petal.Width
1     5.843333    3.057333        3.758    1.199333


sapply를 이용해서 각 컬럼의 데이터 타입을 구할 수도 있다.

> sapply(iris,class)
Sepal.Length  Sepal.Width Petal.Length  Petal.Width      Species 
   "numeric"    "numeric"    "numeric"    "numeric"     "factor"

다음은 조건문을 이용해서 iris 데이터 집합중에서 3보다 큰지 여부를 판단하는 코드이다.

반환값은 행렬이 된다.

> y <- sapply(iris[,1:4], function(x) {x>3})
> class(y)
[1] "matrix"
> head(y)
     Sepal.Length Sepal.Width Petal.Length Petal.Width
[1,]         TRUE        TRUE        FALSE       FALSE
[2,]         TRUE       FALSE        FALSE       FALSE
[3,]         TRUE        TRUE        FALSE       FALSE
[4,]         TRUE        TRUE        FALSE       FALSE
[5,]         TRUE        TRUE        FALSE       FALSE
[6,]         TRUE        TRUE        FALSE       FALSE

주의해야할 사항은 벡터를 반환 하기 떄문에 sapply를 사용할 때는 데이터 타입을 혼합하여 처리하면 안된다.

데이터 타입을 혼합해야 할 경우에는 lapply를 사용하거나 plyr 페키지에 있는 ddply를 사용해야 한다.


▣ tapply() 사용법


tapply{

X 벡터

Index 데이터를 그룹으로 묶을 색인, 팩터를 지정해야 하며 팩터가 아닌 타입이 지정되면 팩터로 형 변환된다.

FUN 각 그룹마다 적용 할 함수

...., 추가 인자. 이 인자들은 FUN에 전달된다.

}

반환 값은 배열이다.


아래의 코드는 간단한 하나의 그룹 1에 대한 합을 나타낸다.

# rep는 1을 10번 반복하라는 뜻이다. 이렇게해서 1:10 까지의 벡터에 factor 데이터 1을 모두 맵핑해서 하나의 그룹으로 만들어 준다.
> rep(1,10)
 [1] 1 1 1 1 1 1 1 1 1 1
> tapply(1:10, rep(1,10),sum)
 1 
55


# 홀수 짝수를 나누어서 연산하는 것이다.
tapply(1:10,1:10%%2 ==1,sum)
FALSE  TRUE 
   30    25 


조금더 복잡한 연산으로 iris 데이터들중에서 length의 평균을 구하는데 그것을 specie에 따라서 구하는 것이다.

> head(iris)
  Sepal.Length Sepal.Width Petal.Length Petal.Width Species
1          5.1         3.5          1.4         0.2  setosa
2          4.9         3.0          1.4         0.2  setosa
3          4.7         3.2          1.3         0.2  setosa
4          4.6         3.1          1.5         0.2  setosa
5          5.0         3.6          1.4         0.2  setosa
6          5.4         3.9          1.7         0.4  setosa
> tapply(iris$Sepal.Length, iris$Species, mean)
    setosa versicolor  virginica 
     5.006      5.936      6.588 


이제 더 복잡한 예제를 살펴보자. 아래의 예제는 추후에 클러스터링 알고리즘을 적용한 다음에 (k-mean clustering)

같은 cluster에 속한 데이터들의 x와 y 좌표의 각가그이 평균을 구하는 용도로 사용 될 수 있다.

#데이터를 아래와 같이 생성 한다.
> m <- matrix(1:8, ncol=2, dimnames=list(c("spring","summer","fall","winter"),c("male","female")))
> m
       male female
spring    1      5
summer    2      6
fall      3      7
winter    4      8

위와 같이 데이터 구조를 생성 했을 때

이때 분기별 과 성별 셀의 합을 구해보자.


상반기 봄, 여름에 대한 남성의 셀 합은 1+2로 구할 수 있다. 그리고 여성의 합은 5+6이다.

하반기 가을, 겨울의 남성의 셀 합은 3+4이고 여성의 합은 7+8이다.


Index를 설정하기 위해서 (n,m)에서 n을 먼저 나열한 뒤 m 값을 나열한다. 

즉, 그룹 (n1, m1), (n2, m2)는 list( c(n1,n2), c(m1,m2))로 표현된다.

> tapply(m, list(c(1,1,2,2,1,1,2,2),c(1,1,1,1,2,2,2,2)),sum)
  1  2
1 3 11
2 7 15


▣ mapply() 사용법


sapply와 유사하지만 다수의 인자를 함수에 넘긴다는 점에서 차이가 존재 한다.

sapply 처럼 행렬과 벡터반환 하게 된다.


1) 여러개의 인자를 보내야 하는 경우에 사용 한다.

2) 어떤 함수에 여러개의 벡터를 입력으로 주고 싶은 경우

a,b를 받아서 처리하는 함수가 있을때

c(....), c(....) 이런식으로 2개의 벡터를 입력으로 보내서 연속적인 처리를 할 수 있다.



mapply: 함수에 리스트 또는 벡터로 주어진 인자를 적용한 결과를 반환 한다.

Fun #실행할 함수

... # 적용할 인자



rnorm의 경우 n개의 데이터를 mean에 대해서 sd인 정규 분포를 생성 한다.


그냥 rnorm을 세번 호출하면 되지만 편의를 위해서 mapply를 이용했다.

세개인자를 rnorm함수로 전달해서 연살을 수행하게 된다.

> mapply(rnorm, c(1,2,3),c(0,10,100),c(1,1,1))
[[1]]
[1] 0.5746612

[[2]]
[1] 10.026820  9.574186

[[3]]
[1]  99.53101  98.92185 101.26264

#iris 데이터의 경우 아래와 같이 수행 된다.
> mapply(mean,iris[,1:4])
Sepal.Length  Sepal.Width Petal.Length  Petal.Width 
    5.843333     3.057333     3.758000     1.199333 

> sapply(iris[,1:4],mean)
Sepal.Length  Sepal.Width Petal.Length  Petal.Width 
    5.843333     3.057333     3.758000     1.199333 

> lapply(iris[,1:4],mean)
$Sepal.Length
[1] 5.843333

$Sepal.Width
[1] 3.057333

$Petal.Length
[1] 3.758

$Petal.Width
[1] 1.199333




R의 문자열 처리 및 비교


문자열 길이 알아내기

nchar

문자열 연결 하기

paste("Everybody", "loves", "you.")

비칸을 이용해서 합쳐지며, 다른 문자를 쓰고 싶으면 seq="" 옵션을 이용 한다.

하위 문자열 추출 하기

substr(string,start,end)

string에서 strat에서 시작하고 end에서 끝나는 문자열을 추출 한다.

구분자료 문차열 추출

strsplit(문자열, 구분자)
반환값은 리스트이고
각각의 리스트 원소들은 백터이다.
이렇게 두개의 수준으로 된 구조가 필요한 이유는 
첫 인자가 문자열로 된 백터일 수 있기 때문이다.
각 문자열은 자신의 하위 문자열들(백터)로 분할 된다.
그리고 나서 이 벡터들이 리스트로 반환되는 것이다.

> path <- "/home/mike/data/trials.csv"
> strsplit(path,"/")
[[1]]
[1] ""           "home"       "mike"       "data"       "trials.csv"
> paths <- c("/home/mike/data/trials.csv","/home/mike/data/data.csv","/home/mike/data/error.csv") 
> path <- "/home/mike/data/trials.csv"
> strsplit(paths,"/")
[[1]]
[1] ""           "home"       "mike"       "data"       "trials.csv"

[[2]]
[1] ""         "home"     "mike"     "data"     "data.csv"

[[3]]
[1] ""          "home"      "mike"      "data"      "error.csv"

하위 문자열 대체하기

sub(old,new,string)
첫 번째 하위 문자열을 대체한다.

gsub(old,new,string)
모든 하위 문자열을 대체한다.

정규 표현식을 없앨려면 fixed = TRUE 옵션을 사용 한다.

문자열 특수문자 보기

cat

문자열의 모든 쌍별 조합 생성

m <- outer(문자열1, 문자열2, paste, seq="")

문자열 비교하는 함수

가능한 함수는 아래와 같고
grepgreplregexprgregexprregexec

grep을 이용해서 
어떤 패턴의 문자열을 
원하는 문자로 변경하는 방법은 아래와 같다.

#아래와 같은 샘플이 있다고 가정하자.
Sample <- c("BU1", "BU2", "BM1", "BD1", "BU3") 

#grep을 수행 할 경우
> grep("^BU", Sample) 
c(1,2,5)

> section <- character(length(Sample)) 
> section[grepl("^BU", Sample)] <- "up" 
> section[grepl("^BM", Sample)] <- "mid" 
> section[grepl("^BD", Sample)] <- "down" 
> section
[1] "up"   "up"   "mid"  "down" "up"

# mapping 방식을 이용한 것도 있다.
# 방법 1
map <- c(BU="up", BM="mid", BD="down") 
section <- unname(map[substring(Sample, 1, 2)]) 
section
[1] "up"   "up"   "mid"  "down" "up" 

# 방법 2
from <- c("BU", "BM", "BD") 
to <- c("up", "mid", "down") 
section <- to[match(substring(Sample,1,2), from)] 
section
[1] "up"   "up"   "mid"  "down" "up"  

IF 와 grep을 혼용해서 사용하는 방법

if(length(grep(x,y))==0)
any(grep(x,y))

반복문을 돌면서 두개의 문자열이 같은지 비교하는 코드

pmatch를 이용한다.

sapply(1:length(singleplace$V1), function(x){

    #pmatch(singleplace$V1[x],multieplace$V1[x])
    pmatch(singleplace$V2[x],multieplace$V2[x])
}
)

기타 Match함수로는 아래의 것들이 있다.

> as.logical(match(x,1,nomatch=FALSE))
[1] FALSE
> 1:10 %in% c(1,3,5,9)
 [1]  TRUE FALSE  TRUE FALSE  TRUE FALSE FALSE FALSE  TRUE FALSE


Plyr package (데이터 조작)


plyr는 reshape2와 마찬가지로 Hadley Wickham에 의해서 개발 되었다.
2011년에 해당 논문이 아래와 같이 출간되었다.

The split-apply-combine strategy for data analysis(2011, Journal of Statistical Software, Vol 40).

데이터들을 쪼개고, 일부부늘 취하며, 어떤 특정 함수를 특정 부분에 적용하고
그리고 결과들을 조합하는 향상된 기능을 제공하는 패키지이다.

그 밖에도
loop의 재배치를 제공한다.

로드법

install.packages("plyr")
library(plyr)

apply 종류가 많다. 하지만 데이터 프레임을 위한것은 없다.
이때 plyr package에 있는 ddply를 이용한다.

함수 제공 형태

  • 배열(a)
  • 데이터 프레임(d)
  • 리스트(l)
  • _는 아무런 출력도 내보내지 않음을 의미함.
{adl}{adl_}ply
입력 데이터 타입출력 데이터 타입

유용한 함수: adply()ddply()mdply()
유용한 유틸리티 함수: transform()mutate()summarise()subset()

Adply()

Adply()는 배열(a)를 받아 데이터 프레임(d)을 반환하는 함수이다. 
입력이 반드시 배열일 필요는 없고 숫자 색인으로 접근 가능 하기만 하면된다.
따라서 데이터 프레임도 입력으로 사용이 가능하다.

apply()의 단점인 문자열 데이터가 섞여 있다면 데이터가 모두 문자열로 반환된다는 점을 개선한 것이다.

adply(
.data, # 행렬, 배열, 또는 데이터 프레임
# 함수를 적용할 방향. 1(행 방향), 2(열 방향) 또는 c(1,2) 행과 열의 모든 방향을 지정 한다.
.margins,
.fun=NULL # .margin 방향으로 잘려진 데이터에 적용할 함수

반환 값은 데이터 프레임이다.

apply와 유사하지만 반환 값이 데이터 프레임이라는 차이가 있다.

예제

#적용 전
 Sepal.Length Sepal.Width Petal.Length Petal.Width Species
1          5.1         3.5          1.4         0.2  setosa
2          4.9         3.0          1.4         0.2  setosa
adply(iris, 1 , function(row){ row$Sepal.Length >= 5.0 & row$Species == "setosa"})

    Sepal.Length Sepal.Width Petal.Length Petal.Width    Species    V1
1            5.1         3.5          1.4         0.2     setosa  TRUE
2            4.9         3.0          1.4         0.2     setosa FALSE
3            4.7         3.2          1.3         0.2     setosa FALSE

예제: 컬럼에 이름 부여

adply(iris, 1 , function(row)
{ 
    data.frame( sepal_ge_5_setosa = c(row$Sepal.Length >= 5.0 & row$Species == "setosa"))
}
)

    Sepal.Length Sepal.Width Petal.Length Petal.Width    Species sepal_ge_5_setosa
1            5.1         3.5          1.4         0.2     setosa              TRUE
2            4.9         3.0          1.4         0.2     setosa             FALSE

Ddply()

ddply()는 데이터 프레임(d)을 입력으로 받아 데이터 프레임(d)을 내보내는 함수 이다.

ddply(
.data,
.variables, #데이터를 그룹 지을 변수명
.fun=NULL
)

반환 값은 데이터 프레임이다.

adply()와 ddply()의 가장 큰 차이점이라면 adply()는 행 또는 컬럼 단위로 함수를 적용하는 반면
ddply()는 .variables에 나열한 컬럼에 따라 데이터를 나눈 뒤 함수를 적용한다는 점이다.

아래와 같이.()안의 컬럼으로 데이터를 묶은 다음에 function안에 있는 함수를 적용 하게 된다.
데이터 프레임의 컬럼의 이름은 sepal.width.mean이며, 각 Species에 대해서 묶고 그것들의 Sepal.Width의 평균을 구하는 코드이다.

ddply(iris, .(Species), function(sub){
    data.frame(sepal.width.mean = mean(sub$Sepal.Width))
})
     Species sepal.width.mean
1     setosa            3.428
2 versicolor            2.770
3  virginica            2.974

추가 조건을 기입한 예제는 아래와 같다.

ddply(iris, .(Species, Sepal.Length > 5.0), function(sub){
    data.frame(sepal.width.mean = mean(sub$Sepal.Width))
})

     Species Sepal.Length > 5 sepal.width.mean
1     setosa            FALSE         3.203571
2     setosa             TRUE         3.713636
3 versicolor            FALSE         2.233333
4 versicolor             TRUE         2.804255
5  virginica            FALSE         2.500000
6  virginica             TRUE         2.983673

Mdply()

m{adl_}ply(), 즉 maply()mdply()mlply()m_ply()함수는 데이터 프레임 또는 
배열을 인자로 받아 각 컬럼을 주어진 함수에 적용하고 그 실행 결과들을 조합한다.
여기서는 이들 중 mdply()에 대해서 살펴본다.

plyr::mdply(
.data, # 인자로 사용할 행렬 또는 데이터 프레임
.fun, # 호출할 함수
)
# 반환 값은 데이터 프레임이다.

Transform, Mutate, Summarise, Subset

데이터 프레임을 반환 한다.

base::transform( 
.data # 변환할 객체
...   # 태그=값 형태의 인자들
)

데이터 프레임 _data에 ...에 지정한 연산을 수행한 뒤 그 결과를 지정한 새로운 컬럼을 추가한 데이터 프레임을 반환한다.

데이터 프레임에 새로운 컬럼을 추가하거나 기존 컬럼을 수정 한다.

plyr::mutate(
.data, # 변환할 데이터 프레임
... # 새로운 컬럼 정의. 컬럼명 = 값 형식
)

변환이 이루어진다는 점은 transform()과 같지만 컬럼명=값 형태로 지정된 연산이 여러 개 있을 때 앞서의 연산 결과를 뒤에 나오는 연산에서 참조할 수 있다는 차이가 있다.

plyr:summarise: 데이터 프레임을 요약 한다.

plyr::summarise(
.data, # 요약할 데이터 프레임
... # 변수=값 형태의 인자들
)

...에 지정된 그룹마다의 요약을 수행한 뒤 그 결과를 저장한 새로운 컬럼이 추가된 데이터 프레임을 반환한다.

subset은 이전 POST 참조

Transform()

transform은 연산 결과를 데이터 프레임의 새로운 컬럼에 저장하는 함수이다.

이를 사용해 baseball 데이터에 각 행이 선수의 몇 년차 통계인지를 뜻하는 cyear 컬럼을 추가해보자.
다음 코드는 데이터를 선수id로 분할하여 그룹 지은 뒤, 각 그룹에세 year의 최솟값과 현재 행의 year값의 차이를 cyear에 저장 한다.

> head(ddply(baseball, .(id), transform, cyear = year - min(year) + 1))

         id year stint team lg   g  ab   r   h X2b X3b hr rbi sb cs bb so ibb hbp sh sf gidp cyear
1 aaronha01 1954     1  ML1 NL 122 468  58 131  27   6 13  69  2  2 28 39  NA   3  6  4   13     1
2 aaronha01 1955     1  ML1 NL 153 602 105 189  37   9 27 106  3  1 49 61   5   3  7  4   20     2
3 aaronha01 1956     1  ML1 NL 153 609 106 200  34  14 26  92  2  4 37 54   6   2  5  7   21     3
4 aaronha01 1957     1  ML1 NL 151 615 118 198  27   6 44 132  1  1 57 58  15   0  0  3   13     4
5 aaronha01 1958     1  ML1 NL 153 601 109 196  34   4 30  95  4  1 59 49  16   1  0  3   21     5
6 aaronha01 1959     1  ML1 NL 154 629 116 223  46   7 39 123  8  0 51 54  17   4  0  9   19     6

Mutate()

base::transform()을 개선한 plyr::mutate() 함수가 있다. 이 함수는 여러 컬럼을 데이터 프레임에 추가할 때 바로 앞서 추가한 컬럼을 뒤에 추가하는 컬럼에서 참조할 수 있어 편리하다. 예를 들어, 아래 코드에서 muate()를 이용해 transform() 예에서처럼 cyear를 계산한 뒤log(cyear)를 log_cyear컬럼으로 추가 한다. 
만약 mutate()가 아닌 transform()을 사용하면 이 경우 에러가 발생 한다.

> head( ddply(baseball, .(id), mutate, cyear=year - min(year)+1, log_cyear = log(cyear)))
> 
         id year stint team lg   g  ab   r   h X2b X3b hr rbi sb cs bb so ibb hbp sh sf gidp cyear log_cyear
1 aaronha01 1954     1  ML1 NL 122 468  58 131  27   6 13  69  2  2 28 39  NA   3  6  4   13     1 0.0000000
2 aaronha01 1955     1  ML1 NL 153 602 105 189  37   9 27 106  3  1 49 61   5   3  7  4   20     2 0.6931472
3 aaronha01 1956     1  ML1 NL 153 609 106 200  34  14 26  92  2  4 37 54   6   2  5  7   21     3 1.0986123
4 aaronha01 1957     1  ML1 NL 151 615 118 198  27   6 44 132  1  1 57 58  15   0  0  3   13     4 1.3862944
5 aaronha01 1958     1  ML1 NL 153 601 109 196  34   4 30  95  4  1 59 49  16   1  0  3   21     5 1.6094379
6 aaronha01 1959     1  ML1 NL 154 629 116 223  46   7 39 123  8  0 51 54  17   4  0  9   19     6 1.7917595

Summarise()

transform이 인자로 주어진 계산 결과를 새로운 컬럼에 추가한 데이터 프레임을 반환하는 반면,
summarise()는 계산 결과만을 담은 새로운 데이터 프레임을 반환 한다.

baseball 데이터에서 각 선수의 최초 데이터가 몇 년도인지를 조사해보자.
아래 예에서는 데이터를 id마다 그룹지은 뒤 각 그룹마다 year의 최솟값을 계산한 minyear 컬럼을 생성 했다.
연산 시 사용할 함수로 summarise()를 지정했으므로 그룹을 짓는 변수인 id와 각 그룹의 요약 값 minyear만 저장된 데이터 프레임이 반환됐다.

> head (ddply(baseball, .(id), summarise, minyear=min(year)))
         id minyear

1 aaronha01    1954
2 abernte02    1955
3 adairje01    1958
4 adamsba01    1906
5 adamsbo03    1946
6 adcocjo01    1950

만약 여러 요약 값을 구하고 싶다면 요약 값 계산을 계속 나열한다.

> head (ddply(baseball, .(id), summarise, minyear=min(year), maxyear=max(year)))
         id minyear maxyear

1 aaronha01    1954    1976
2 abernte02    1955    1972
3 adairje01    1958    1970
4 adamsba01    1906    1926
5 adamsbo03    1946    1959
6 adcocjo01    1950    1966


'AI > R Basic' 카테고리의 다른 글

Apply 함수 (데이터 조작)  (1) 2015.10.03
R의 문자열 처리 및 비교  (0) 2015.10.03
Reshape2 pacakge (데이터 조작)  (0) 2015.10.01
R 자주 사용하는 팁 및 한글 주석 깨짐 해결  (2) 2015.07.30
R의 철학  (0) 2015.04.17

Reshape2 pacakge (데이터 조작)


해당 페키지는 Handley Wickham에 의해서 2007년에 개발된 것이다.

해당 기능은 아래의 저널에 게재 되었다. 해당저널은 JCR 기준 상위 5%이내의 저널이다.

Reshaping Data with the reshape Package. Journal of Statistical Software



reshape2 package를 이용하는 방법

CRAN에서 제공하는 페키지이다. 좀더 향상된 기능의 자르고 쪼개고 재결합하는 기능을 담고 있다.


우리는 때때로 Wide-format 또는 Long-format을 시시각각 원한다.



Wide-format data

ozone   wind  temp
1 23.62 11.623 65.55
2 29.44 10.267 79.10
3 59.12  8.942 83.90
4 59.96  8.794 83.97



Long-format data

    variable  value
 1     ozone 23.615
 2     ozone 29.444
 3     ozone 59.115
 4     ozone 59.962
 5      wind 11.623
 6      wind 10.267
 7      wind  8.942
 8      wind  8.794
 9      temp 65.548
 10     temp 79.100
 11     temp 83.903
 12     temp 83.968


wide-format의 경우 데이터를 분석 할때 주로 쓰이고

long-format은 그외의 모든 경우에 이용된다.

ggplot2

plyr

modeling functions ( lm(), glm(), gam(), etc)


해당 페키지의 key function은 아래와 같다.


melt: wide-format data를 읽어서 그것을 long-format으로 변경함.

cast: long-format data를 읽어서 그것을 wide-format으로 변경함.



melt 설명

melt은 value와 variable로 구분되서 동작한다.

즉 변수(variable)에 대해서 값(value)를 매칭하는 방식으로 넓게 퍼진 데이터를 길게 변경한다.


melt default의 동작 방식은

numeric data를 포함하는 모든 열들이 변수(variable)이 된다.


원래 프레임의 열이 6개고 모든 열이 numeric data라면 

그리고 행이 135개라면 이것을 default melt으로 변경하면

135 * 6 = 918개의 데이터로 길어 지게 된다.



만약 특정 열에대해서 다른 값들을 정리하고 싶다면, vars = c("x","y") 의 옵션을 사용 한다.

즉, X,Y를 ID 변수로 설정하고 이것의 값들은 변경하지 않는다. 


$원래의 행의 수 \times ( 총 열의수 - ID변수의 수)  = 새로 생성될 행의 수$


> aql <- melt(airquality, id.vars = c("month","day"))


실행결과

> str(airquality)
'data.frame':	153 obs. of  6 variables:
 $ ozone  : int  41 36 12 18 NA 28 23 19 8 NA ...
 $ solar.r: int  190 118 149 313 NA NA 299 99 19 194 ...
 $ wind   : num  7.4 8 12.6 11.5 14.3 14.9 8.6 13.8 20.1 8.6 ...
 $ temp   : int  67 72 74 62 56 66 65 59 61 69 ...
 $ month  : int  5 5 5 5 5 5 5 5 5 5 ...
 $ day    : int  1 2 3 4 5 6 7 8 9 10 ...
> aql <- melt(airquality, id.vars = c("month","day"))
> str(aql)
'data.frame':	612 obs. of  4 variables:
 $ month   : int  5 5 5 5 5 5 5 5 5 5 ...
 $ day     : int  1 2 3 4 5 6 7 8 9 10 ...
 $ variable: Factor w/ 4 levels "ozone","solar.r",..: 1 1 1 1 1 1 1 1 1 1 ...
 $ value   : num  41 36 12 18 NA 28 23 19 8 NA ...


이름변경 방법

aql <- melt(airquality, id.vars = c("month", "day"),
  variable.name = "climate_variable", 
  value.name = "climate_value")
head(aql)




#   month day climate_variable climate_value

# 1     5   1            ozone            41

# 2     5   2            ozone            36

# 3     5   3            ozone            12

# 4     5   4            ozone            18

# 5     5   5            ozone            NA

# 6     5   6            ozone            28



cast 함수의 이해


인간이 이해하기에는 wide-format data가 가장 좋다.

따라서 스크래치 단계에서 많이 사용된다.


cast는 데이터 타입별로 지원한다. 하지만 여기서는 data.frame에 대해서 주로 다루겠다.


aql <- melt(airquality, id.vars = c("month", "day"))

aqw <- dcast(aql, month + day ~ variable)

head(aqw)


#   month day ozone solar.r wind temp

# 1     5   1    41     190  7.4   67

# 2     5   2    36     118  8.0   72

# 3     5   3    12     149 12.6   74

# 4     5   4    18     313 11.5   62

# 5     5   5    NA      NA 14.3   56

# 6     5   6    28      NA 14.9   66


head(airquality) # original data


#   ozone solar.r wind temp month day

# 1    41     190  7.4   67     5   1

# 2    36     118  8.0   72     5   2

# 3    12     149 12.6   74     5   3

# 4    18     313 11.5   62     5   4

# 5    NA      NA 14.3   56     5   5

# 6    28      NA 14.9   66     5   6



동작 방식을 그림으로 정리하면 아래와 같다.




추가적인 정보는 아래의 명령어로 알 수 있다.

Feature (Attribute) Selection


어떤 feature를 뽑아야 좋은 model을 생성 할 수 있을까?

해당 질문은 매우 어려운 문제이다.



해당 분야에 대한 깊은 지식이 없는 사람은 feature를 선택하기가 쉽지 않다.

이것을 자동으로 결정해 준다면 매우 좋을것이다. 이러한것을 목표로 하는 것이 feature selection 이다.



Feature selection이란 결국 가장 관련있는 feature들의 subset을 골라 내는 작업을 말한다.

이것은 Dimensionality reduction과 유사해 보일 수 있지만 그렇지 않다.



Feature Selection을 통해서 해결 되는 문제점들


관련 없는 데이터 제거

중복성 제거

정확도에 높은 모델에 기여되는 feature들이 무엇인지를 알아냄

심지어 어떤 feature들은 참여 됨으로써 정확도를 떨어뜨림 이런것들을 찾아냄


feature들을 최대한 적게 써서 model을 만드는 것이 좋다.

왜냐하면, 성능도 좋고 복잡하지 않으므로 이해하기도 쉽다. 그리고 설명도 쉽게 된다.



Feature Selection Algorithms

일반적으로 feature 선택 알고리즘은 세가지로 구분 된다.


filter method

wrapper method

embedded method


- Filter Method

각각의 feature들에 통계적인 점수를 부여하는 방법이다.

그리고 각각의 feature들은 score에 의해서 rank매겨 진다.


Example of some filter methods include the Chi squared test, information gain and correlation coefficient scores.



- Wrapper Method

search problem으로써 feature들의 집합을 선택한다.

An example if a wrapper method is the recursive feature elimination algorithm.




- Embedded Method


어떻 feature가 가장 크게 기여하는지를 찾아내는 방법이다.

Examples of regularization algorithms are the LASSO, Elastic Net and Ridge Regression.




Feature Selection Tutorials and Recipes

We have seen a number of examples of features selection before on this blog.


Weka: For a tutorial showing how to perform feature selection using Weka see “Feature Selection to Improve Accuracy and Decrease Training Time“.

Scikit-Learn: For a recipe of Recursive Feature Elimination in Python using scikit-learn, see “Feature Selection in Python with Scikit-Learn“.

R: For a recipe of Recursive Feature Elimination using the Caret R package, see Feature Selection with the Caret R Package









Single Attribute Evaluator: infoGainAttributeEval

Evaluate attribute based on information 


'AI > Theory' 카테고리의 다른 글

Fast Convolution  (3) 2020.02.13
Loss functions  (0) 2018.08.09
Ensemble Method  (0) 2015.09.15
Active Learning  (0) 2015.08.19
기계학습을 구현하기 위해서 어떤 언어가 좋은가?  (0) 2015.07.11

Ensemble Method


Random Forests


decision tree classifier들을 위해서 디자인 된것으로써 다중 decision tree를 이용 한다.

각각의 트리들은 독립적인 랜덤 셋 백터를 생성하게 된다.

'AI > Theory' 카테고리의 다른 글

Loss functions  (0) 2018.08.09
Feature (Attribute) Selection  (0) 2015.09.15
Active Learning  (0) 2015.08.19
기계학습을 구현하기 위해서 어떤 언어가 좋은가?  (0) 2015.07.11
순차데이터 분석을 위한 Hidden Markov Model (HMM)  (0) 2015.06.08

Active Learning



semi-supervised machine learning 


이것은 interactively query를 이용해서 desired output을 새로운 데이터 포인트에서 획득 하는 방식을 의미한다.



사용하는 상황은


unlabeled data가 매우 많아서 수작업으로 그것을 라벨링 하기 어려울 때

active learning 알고리즘을 이용해서 지도자에게 질의 하여 라벨링을 할 수 있다.


즉 uncertainty sampling 데이터가 많은 상황에서 사용하는 방법이다.



좋은점은

학습에 필요한 라벨된 데이터의 양을 줄일 수 있다.

이유는 학습자가 능도적으로 데이터를 선택하기 때문이다.



'AI > Theory' 카테고리의 다른 글

Feature (Attribute) Selection  (0) 2015.09.15
Ensemble Method  (0) 2015.09.15
기계학습을 구현하기 위해서 어떤 언어가 좋은가?  (0) 2015.07.11
순차데이터 분석을 위한 Hidden Markov Model (HMM)  (0) 2015.06.08
Anomaly Detection  (0) 2015.06.07


R 자주 사용하는 팁 및 한글 주석 깨짐 해결


1. 자주 사용하는 RStudio 단축키와 명령

    –  Console 화면에서 Ctrl_l (Ctrl_소문자L) : 화면을 깨끗하게 지웁니다.

    –  rm(list=ls(all=TRUE)) : 작업 영역을 깨끗이 청소 합니다.

    –  Ctrl_+ : 화면에 표시되는 문자의 크기를 크게 합니다.

    –  Ctrl_- : 화면에 표시되는 문자의 크기를 작게 합니다.

    –  "Tools -> Global Options…" 메뉴를 선택한 후 "General" 메뉴에서

        "Default working directory (when not in a project):" : RStudio에서 사용할 작업용 폴더를 지정 합니다.

        "Default text encoding: " : ~.R 파일의 인코딩을 지정 합니다.  저는 "UTF-8"을 지정하여 사용하고 있습니다.

 

    –  RScript 편집 화면에서 Ctrl_Enter : 커서가 있는 라인을 Console 화면에서 실행 합니다.

    –  ? ~ : help(~)와 동일한 기능으로 함수, 데이터셋 등에 대한 도움말을 표시 합니다.

        사용 예 1)  ? iris                 : iris 데이터셋에 대한 도움말

        사용 예 2)  ? summary         : summary 함수에 대한 도움말

    –  ??  ~ : help.search("~")와 동일한 기능으로 함수 등의 이름을 명확히 알지 못할 경우, 검색을 할 때 사용 합니다.

    –  help(package = "~"), library(help = "~") : 패키지(라이브러리) 도움말로 제공하는 함수 목록을 확인할 수 있습니다.

        사용 예)  library(help = "stats") : stats 패키지의 도움말 화면을 표시 합니다.

    –  methods(~) : ~이 대표하는 함수들의 목록을 표시 합니다.

        사용 예)  methods(plot) : 다양한 데이터 타입을 사용하여 챠트를 그려주는 함수명을 확인할 수 있습니다.

                     시계열 데이터를 그려주는 plot.ts(~) 대신에 

                      대표 함수명인 plot(~)를 사용하면 내부적으로 plot.ts(~)가 실행이 됩니다.

    –  args(~) : ~ 함수의 인자 정보를 조회 합니다.

        사용 예) args(lm) : 회귀분석에서 사용하는 lm 함수의 인자 정보를 확인 합니다.

    –  attributes(~) : 데이터셋의 인자 정보를 조회 합니다.

    –  TAB : 자동 완성 기능으로 console 창에서 몇글자를 입력한 후 TAB키를 누르면

                입력한 글자로 시작되는 함수 목록을 보여 주어 자동 완성 기능을 사용할 수 있도록 합니다.

 

 

2.  문자셋 관련 사항

  –  UTF-8 문자셋으로 저장된 ~.R 파일을 사용할 경우

      –  "Tools -> Global Options…" 메뉴를 선택한 후 "General" 메뉴에서

        "Default text encoding: " : ~.R 파일의 인코딩을 지정 합니다.  "UTF-8"을 지정하세요.

 


  –  UTF-8 문자셋으로 저장된 데이터 (csv 파일 등)을 읽을 경우

     read.table(생략, encoding = "UTF-8")                            #— 문자셋 정보를 명시 합니다.

 

  –  R의 인코딩 정보 확인

      localeToCharset()

      Sys.getlocale()

  –  오류 메시지를 영문으로 보기 (영문 오류 메시지가 명확하고 구글 등의 검색을 통해서 확인하기가 편리 합니다.)

      Sys.setlocale("LC_ALL", "English_United States.1252")  #— 영문 문자셋을 지정 합니다.

      Sys.setlocale()                                                          #— 원래 디폴트로 설정된 문자셋으로 복구 됩니다.

  –   데이터의 인코딩 정보 확인

      Encoding(~)

  –  문자열의 인코딩 변환

      iconv(~, "CP949", "UTF-8) #— "CP949"로 인코딩된 ~라는 데이터에 저장된 문자열을 "UTF-8"로 인코딩된 문자열로 변환

 

  –  "Notepad++" 텍스트 편집기 : 오픈소스로 제공되는 편집기로 사용이 편리 합니다.

      –   다운로드 사이트 : http://notepad-plus-plus.org/

      –   파일의 문자셋 변경 방법

           편집기의 우측 하단 영역에 문자셋이 표시 됩니다.  (ANSI, ANSI as UTF-8 등)

              윈도우의 디폴드 문자셋인 CP949를 사용할 경우 ANSI로 표시됨

              BOM이 없는 UTF-8 문자셋을 사용할 경우 ANSI as UTF-8로 표시됨

           "인코딩" 메뉴에서

              "ANSI로 변환" 메뉴 선택                    : UTF-8 문자셋으로 저장된 파일을 CP949 문자셋으로 변환 

              "UTF-8 (BOM 없음)로 변환" 메뉴 선택 : CP949 문자셋으로 저장된 파일을 UTF-8 문자셋으로 변환

기계학습을 구현하기 위해서 어떤 언어가 좋은가?


원제: Best Programming Language for Machine Learning


아래는 기계학습을 위해서 사람들이 선택한 언어를 나타낸다.




We ran a brief analysis on the tools Kagglers used and wanted to share the results.  The open source package R was a clear favorite, with 543 of the 1714 users listing their tools including it.  Matlab came in second with 218 users.  The graph shows the tools that at least 20 users listed in their profile.


Machine Learning Languages


MATLAB/Octave

깊이 있게 내부를 구현해보고 싶을때 사용할 수 있는 언어이다.

matrix 연산에 특화된 도구이다.

R

statistical analysis에 특화된 언어이다.

machine learning 알고리즘도 많이 구현 되어있다.

확장성도 용이하다.


Python

NumPY로 MATLAB과 같은 행렬 연산 가능

SciPy로 machine learning 알고리즘 구현 가능


Java-family / C-family

여기에는 강건한 라이브러리르 들이 많이 있다.

Weka(java), LibSVM(C++) 등이 하나의 예이다.

성능관점에도 C는 유용하다.





Programming Languages for Machine Learning Implementations

Speed

python과 java 같은 경우 garbage collection이 문제이다.


Programming Ease

매우 주관적인 부분이지만 그래도 차이는 존재 한다.

1) Syntax: 일반언어처럼 자연스러우며 간결해야 한다. 

2) Library support: linear algebra, graphics, IO 등과 같은 라이브러리를 효율적으로 제공 해줘야 한다.

3) Built in Functionality: 자료구조, module 등을 의미 한다.

4) Scalability: 

5) Familiarity


The existing significantly used machine learning code bases seem to favor lower level languages.

  1. Weka is one of the best known general purpose machine learning toolkits. It is implemented in Java and has far more algorithmic coverage than any other system I know of.
  2. LibSVM is implemented in C++ and Java.
  3. SVMlight is implemented in C.
  4. C4.5 is implemented in C.
  5. SNNS is written in C.







'AI > Theory' 카테고리의 다른 글

Ensemble Method  (0) 2015.09.15
Active Learning  (0) 2015.08.19
순차데이터 분석을 위한 Hidden Markov Model (HMM)  (0) 2015.06.08
Anomaly Detection  (0) 2015.06.07
Unsupervised Learning: Cluster Analysis  (3) 2015.06.03

R에서 EPS 이미지 생성


EPS 이미지로 저장하는 방법을 다루겠다.


postscript(file="plot.eps", onefile=FALSE, horizontal=FALSE)


dev.off()



If you are using ggplot2 to generate a figure, then a ggsave(file="name.eps") will also work

'AI > R ggplot2' 카테고리의 다른 글

CDF stat_ecdf  (0) 2016.12.21
ggplot2: 그래픽 라이브러리  (0) 2016.03.20
ggplot2 package를 이용한 Graph 그리기  (0) 2015.02.16
R 그래프 페키지 종류  (0) 2014.11.05

순차데이터 분석을 위한 Hidden Markov Model (HMM)



분석에 사용되는 feature들이 시간적 특성을 지닌다.

e.g., 지진파, 음성, 주식 거래량, 온라인 필기 문자 등


이러한 데이터를 sequential data 또는 context-dependent data라고 부른다.




시간성의 없는 데이터 선후 관계를 바꿔도 아무런 의미 변화가 없다.





시간성이 있는 데이터의 경우 선후 관계가 매우 중요해서

그것을 변경하면 새로운 결과가 나오게 된다.





HMM을 배우기 전에 기본적으로 Markov model이 무엇인지 부터 보자.


러시아 수학자 Andrey Markov 제안

시간 t에서의 관측은 가장 최근 r개 관측에만 의존한다는 가정 하의 확률 추론




예제

최근 4일간의 날씨 정보를 가지고 오늘 날씨를 추론하는것을 생각해 보자.


해 해 해 비 

1차 마코브 모델을 사용하면 어제의 날씨 변화만 오늘에 영향을 미치므로 아래와 같이 4가지 경우가 모두 같게 된다.





1차 마커브 모델을 표로 표현하면 아래와 같다.


오늘 비 였을때 내일 {비,구름,해}의 확률을 모두 합하면 1이 되야한다. 계산해 보면 1이 된다.

하지만, 내일 {비}라고해서 오늘 {비,구름,해}일 확률의 합이 1은 아니다. '역'의 관계에서는 어떠한 시간적 의미도 없기 때문이다.


여기서 2차 마코브 모델을 고려한다고 생각해보자.

선후 관계가 있으므로 순서가 있는 경우다. 즉 순열이며

{비,비,비}와 같은 중복을 허용한다.

따라서 2차 마코브 일때의 경우의 수는 아래와 같다.





위 표를 이용해서 3x3 matrix를 생성한다. 이것을 이용해서 마크브 모데의 상태전이도를 만들게 된다.



아래와 같은 조건을 만족 한다.



마코브 모델을 이용해서 무엇을 할 수 있을 까?


관측백터 x를 구할 수 있게 된다.


P(x|마코브 모델) = P(x|A)라는 식이 나온다.

여기서 마코브 모델을 나타내는 A는결국 joint probability가 되므로


마크모델 A와 관측 벡터 O간의 독립이면 P(O|A) = P(O)로 기술 할 수 있다.

그리고 joint probability가 갑자기 3->4로 넘어갈때 간단해 지는데 이러한 이유는

DAG 그래프 형태로, 즉 노드가 바로 직전의 부모에만 의존적인 베이시안 네트워크 구조를 가지기 때문이다.

즉 우리가 가정한 마코브는 1차원 바코브기 때문에 조인트 확률이 바로 직전의 노드에만 의존적이므로 뒷 부분을 모두 다 날려 버릴 수 있게 된다.



1차 마코브라도 계산을 위해서는 초기 확률이 필요하다.

초기 확률은 해가 되기위한 "파이3" 이다

곱의 확률이기 때문에 확률이 너무 적게 된다.





히든 마코브와 그냥 마코브와의 근본적인 차이점.


- 마코브 모델은 상태를 나타내는 노드에 관측 (비,해,구름) 그 자체가 들어 있다. 즉 상태를 볼 수 있다.

- HMM에서는 상태를 볼수 없게 했다. 즉 상태를 은닉 하게 된다.



동기


r차 마크브에서는 m^r (m-1)개의 매개변수가 생성 된다.

차수에 따라 모델의 크기가 너무 크게 증가 한다.


HMM

- 차수를 미리 고정하지 않고 모델 자체가 확률 프로세스에 따라 적응적으로 정함

- 따라서 아무리 먼 과거의 관측도 현재에 영향을 미침

- 즉 P(해 |비,비), P(해 | 비,비,비) , P(해,비,비,...,비) 모든 것에 따라서 확률 값이 다르게 된다. 즉 특정 어떤 차수에 고정적이지 않다.

- 이런 뛰어난 능력에도 불구하고 모델의 크기는 감당할 정도 이다.


비결

- 상태를 감추자





상태를 가지는 대신에 모든 노드가 모든 상태를 가질 수 있는 확률을 가지게 된다.


이용방법

예를 들어서 고나측 데이터로써 해->해->비->구름 이렇게 4일간의 관측 데이터가 존재한다고 하자.


그냥 마코브 모델을 사용한다면, 저러한 상태 전이는

s3 -> s3 -> S1 -> s2가 된다.


하지만 HMM의 경우

모든 상태에서 {비,해,구름}이 관측이 가능 하므로 가능한 시퀀스가 여러개 존재한다.

상태가 3개이니

3*3*3*3 의 경우의 수가 나온다.

3^4 = 81


이중에서 하나만 골라서 계산해 보면 

s3-> s3 -> s1 -> s2




HMM의 예제 (1) 


n개의 항아리에 공의 색깔 m개가 존재함

이러한 구성이 아래의 그림처럼

항아리 3개이고 공의 색깔은 4개이다.





이제 관측 백터에 대한 확률을 하나를 구해보자.


모델이 학습을 통해서 위와 같이 생성 되었다면,


관측 백터 O가 {진파, 연파, 검, 흰}이고

상태전이가 s1->s1->s1->s1 일때의 확률을 구하라





구하면 위와 같다.


그러면, 관측 백터 O가 {진파, 연파, 검, 흰} 나올 확률은 어떻게 구할까?


상태가 3개이고 4단계 이므로 나오는 확률의 경우의 수는 3^4 즉 81개이다.


이 81개의 확률을 모두 더해주면 관측 백터 O의 확률이 된다.


더하기다. 즉 그냥 Markov 모델은 곱하기인데, HMM은 더하기로 연산이 된다. 당연히 확률이 클 수 밖에 없다.




HMM의 예제 (2) 


여자친구의 삶 [Wikipedia]


-여자 친구의 일상을 관찰, V={산책, 쇼핑, 청소}

- 날씨는 {해,비} 2가지 상태 존재

- 수집한 정보는 아래와 같다.




답할 수 있는 문제

- 그녀가 일주일 연속으로 쇼핑만 할 확률은 ? (평가문제)

- { 산책, 산책, 청소 } 이렇게 했다면, 3일동안 날씨는 각각 어떠했는가 ? (디코딩 문제)

- { 산책, 산책, 청소} 했다면 오늘과 내일은 무엇을 할까? {복합적인 문제}




HMM의 구성요소와 세가지 문제


- 모델을 알고 있을 때 확률을 추론하라.(평가)

- 어느 것이 상태인가? 상태는 몇가지 인가? (아키텍쳐 설계)

- 확률 분포는 어떻게 구하나? (학습)


아키텍쳐 설계

HMM은 통상 가중치 방향 그래프로 표현 한다.

노드가 상태가 된다.

상태로 사용할 것이 명확한 경우도 있지만 그렇지 않은 경우도 있다.



대표적인 아키텍처

어고딕 모델과 좌우 모델

응용의 특성에 따라 적절한 아키텍처 선택이 중요

예를들어 음성인식은 좌우 모델이 좋다.




세개의 변수 결정

- 상태전이 확률 행렬 구성

- 관측 행렬 구성

- 초기 확률 백터 구성



세가지 문제점


평가: 모델 A가 주어졌을 때 관측 백터 O를 얻었을 때의 확률 P(O|A)는?

디코딩: 모델 A가 주어진 상화에서, 관측 벡터 O를 얻었을 때 최적의 상태열은 ? 역으로 가는 형태이다.

학습: 훈련집합을 가지고 모델 A를 도출 하는 과정이다.




HMM으로 Classficiation을 하는 방법은


Class 라벨마다 HMM을 생성해서 가장 높은 값을 가지는 Class 라벨로 prediction 하는 방법이 있다.



세가지 문제점에 적용 가능한 방법


평가: 동적 프로그래밍 이용

디코딩: 동적 프로그래밍 이용 (Viterbi 알고리즘)

학습: EM 알고리즘 (Baum-Welch 알고리즘)



평가: 동적 프로그래밍 이용


계산은 할 수 있어도

만약 관측 백터가 시퀀스 4이고 상태가 2이라면 (여자친구의 생활 패턴 문제)


2^4 = 16개를 계산 해야한다.


이것을 일반화 하면

상태수 n 관축 백터 O의 길이 T에 대해서


n의 t제곱 * t가 된다. 마지막 T는 더하기 연산 때문에 발생



동적 프로그래밍을 통한 중복 계산의 제거




















'AI > Theory' 카테고리의 다른 글

Active Learning  (0) 2015.08.19
기계학습을 구현하기 위해서 어떤 언어가 좋은가?  (0) 2015.07.11
Anomaly Detection  (0) 2015.06.07
Unsupervised Learning: Cluster Analysis  (3) 2015.06.03
Deep Learning  (0) 2015.06.03

Anomaly Detection




비정상적인 데이터를 찾는 문제이다.


응용분야는

신용카드 사기, 전화 사기, 네트웍 침입, Fault Detection 등이 있다.



Challenge

얼마나 많은 outlier를 찾을 것인가?

unsupervised이므로 찾기가 어렵다.


가정

정상저인 데이터가 비정상보다 확연하게 더 많다.



Types of anomaly detection schemes

- model based or statistical approaches

- distance based

- Density based



1) model based or statistical approaches

일단 모델을 생성하던가 어떤 잘 알려진 분포를 따라야 한다.


normal distribution과 같이 특정 파라메터로 분포를 설명 할 수 있어야 한다.

이럴때 outlier란 결국 probability가 낮은 분포에 속하는 데이터가 된다.



아래와 같은 분포인 Gaussian distribution이 많이 사용된다.

파라메터는 mean과 standard deviation이다.

위는 PDF로

N(0,1)의 값을 나타낸다.

2차원 PDF를 나타내므로, 결국 원래 값은 1차원 분포인 것이다. 변수가 x 하나이다.


Statistical 접근법의 한계점

Attribute가 1개이다. 대부분

데이터 분포가 잘알려진 분포를 따르기가 쉽지 않다.

고차원 데이터 분포에서는 적용하기가 쉽지 않다.




2) Distance based approach


멀리 떨어지면 아웃라이어
k-nearest neighbor를 쓸 수 있다.
장점: 간단
단점:
복잡도 O(m^2)
m by m matrix를 생성 해야 내기 때문이다.

k와 같은 파라메터에 너무 민감함
하나의 분포에서 밀도가 매우 다양하게 분포되어 있다면 잘 동작하지 않는다.
즉, 같은 데이터 분포안에서 특정 점들음 엄청 밀도가 높고 특정 점들은 밀도는 낮지만 어쨋든 모여있다면 명백히 두 점들의 분포는 각각의 클러스터를 생성할 수 있게 되어야 한다. 이렇게 같은 분포 안에서 밀도가 서로 다르게 나타나지면 밀도가 낮은쪽은 몽땅다 아웃라이러로 처리 되어 진다.

5개로 설정하면 일단 C는 아웃라이어 처럼 보인다.


하지만 문제는 1개로 줄여 버리면 c가 아웃라이러로 판별되지 않는다.

즉, K에 너무 민감하다.



k를 너무 높여도 문제가 발생 한다.



밀도가 서로 다를때가 문제가 심하다.


C는 아웃라이어다.


하지만 D와 A는 아웃라이로 검출하지 못한다.

전체적인 거리만 따질것이 아니라, 자신 주위의 밀도도 같이 고려 해야 한다.




- Density based approach



neighbor 안에 드는 모든 node들에 대한 distance의 평균을 구한 다음

그것의 역수를 취해서 density를 계산하여 outlier를 판별 한다.


한계점은 역시나 서로다른 밀도로 섞여 있는 경우 적합하지 않다.



- Improved density based approach

주변의 이웃들도 같이 밀도를 고려 하는 방법이다.




relative density에 역수를 취해서 outlier를 검출 하는데 사용 한다.


위 식의 의미는 아래와 같다.

N = 3이라고하면


나와 내 이웃 3개와의 Density의 평균 / 나의 이웃 3개 들의 Density 평균


이렇게 되면 이 값이 크다는 의미는  나의 덴시티가 내 이웃들의 덴시티 보다 크기 때문에 나는 더더욱 아웃라이어이지 않을 확률이 높다는 것이다.

작다는 의미는 내 덴시티보다 이웃들의 덴시티가 더 크니 위에서 발견못한 D같은 point를 아웃라이어로 판별 할수 있다.

따라서 저 값을 역수로써 사용하면 anomaly detection의 지표로 사용 할 수 있다.




3) Density based LOF Approach 


Local Outlier Factor (LOF)





Outliers in Lower Dimensional Projection


고차원에서는 빈공간도 너무 많고, proximity의 개념 자체가 잘동작 하지 않는다.

따라서 대부분의 point들이 outlier로 판별되는 문제가 존재 한다.


이것을 방지하기 위해서 Lower dimensional projection을 수행 해야 한다.








Unsupervised Learning: Cluster Analysis



데이터들의 분포에서 어떠한 그룹을 찾는 것이 cluster Analysis 이다.


대표적인 Unsupervised learning 방법이다. 정답을 모르는 상태에서 데이터들의 분포를 가지고 그룹을 결정 하기 때문이다.




위와 같이 그룹을 생성 했을때 Cluster안에서의 각각의 Data들 사이의 거리는 작아야한다. 즉 조밀한 배치를 이루고 있어야 한다.


Cluster들간의 거리는 최대한 먼 상태가 좋은 것이다.




기본적인 Clustering의 종류는 다음과 같이 나뉜다.


1) Partitional Clustering

각각의 그룹이 overlapping 되어지지 않고 정확히 하나의 그룹으로 구성되는 형태를 의미한다.





2) Hierarchical Clustering

계층적인 트리 형태로 구성되는 것을 말하며 그룹이 중첩되서 클러스터를 이루는 형태를 말한다.





Clustering Algorithms: K-means and its variants


가장 기본적인 Clustering 알고리즘이다.


초기 K를 정해서 각각의 초기 중심을 잡는다.

각각의 초기 중심을 기점으로 흩어져있는 데이터들중에서 가장 가까운 것을 그 중심을 기준으로한 클러스터에 편입 시킨다.

각각의 클러스터에대한 중심을 새로 계산 한다.


모든 중심이 변하지 않을때까지 반복한다.

하지만 어떤 경우에서는 끝나지 않을 수도 있다. 

따라서 몇 %이상 변하지 않을 때 까지나, SSE를 계산해서 변화량을 볼수도 있고

iteration 횟수를 고정해서 줄 수도 있다.



특성은 아래와 같다.

partitional clustering approach: 정확히 하나의 그룹에만 들어가지는 형태이다. 오버랩핑 되지 않는다.

Each cluster is associated with a centroid (or center point)

Each point is assigned to the cluster with the closest centroid

Number of clusters, K, must be specified 

The basic algorithm is very simple


- Complexity O(n*K*I*d)

n: number of points

K: number of clusters

I: number of iterations

d: number of attributes


- 어려운점

K값을 알기가 어렵다.

distance measure에 너무 민감하게 작용 한다.

초기 값의 선택이 매우 중요 한다.



결국 K-means cluster의 성능은 초기 center point의 선택으로 좌우 하는데,

성능 측정 지표가 있어야 계속 중심 포인트를 변경해 가면서 테스트를 수해할 수 있다.


제안된 성능 측정 방식은




Sum of Squared Error (SSE) 

방법이다.


아중 시크마의 의미는 아래와 같다


만약 K가 3이라고 가정하자.

즉 3개의 클러스터로 분할하고 싶어서 3이라고 준것이다.


각각의 클러스터에서의 중심 즉 평균과 각각의 데이터 포인트들간의 distance를 계산해서 제곱한다음 합을 구해주게 된다.





이렇게 해서 다 더하기 때문에 위와 같은 더블 시크마 공식이 나오게 된다.


이렇게해서 구한 SEE를 가지고 초기 seed를 변경해 가면서 

더 좋은 cluster를 찾을 수 있게 된다.


하지만 SSE measure의 한계점도 존재한다.

이것은 Intra개념만을 포함한 수치이다.

즉 클러스터들 간의 거리는 고려하지 않았다. inter 개념이 빠졌기 때문에

SSE가 최적의 k-means cluster를 찾는 가장 좋은 measure 라고 할 수 없다.




초기 중앙 점을 찾는 문제


  • 여러번 수행한다.

  • 계층적 클러스터를 구성해서 시각적으로 분석한 다음 적절히 K를 설정한다.

  • 의도적으로 더 많은 K를 설정한 다음 이중에서 적절히 초기 K를 선택한다. 즉 가장 넓게 분할되는 K를 선택 하는 거이다.

  • Bisecting K-means 알고리즘을 수행 한다. Not as susceptible to initialization issues


Bisecting K-means



하나의 클러스터에서 출발해서 반복하는 숫자만큼 클러스터를 분할하는 방법이다.

bisection에서 가장 작은 SSE를 가지는 2개의 클러스터를 list of clusters로 추가한다.


이 작업을 cluster 리스트가 K 클러스터를 포함할 때 까지 반복 하게 된다.


아래는 Bisecting K-means를 수행 했을 때의 그림이다.



기본적인 K-means algorithm의 문제는 바로 empty cluster의 생성 이다.

즉 클러스터를 계속 생성하는데, 가장 가까운 data가 하나도 없는 경우가 과정중에 자주 발생한다.

이때 어떻게 처리할 것인가?


해결방법

가장 멀리있는 포인트로 새로 시작

가장 높은 SSE로 새로 출발

동시에 몇개의 empty cluster가 발생하면 위가정을 몇번 더 반복 한다.


한번에 하나씩만 Center들을 업데이트 하는 방법. 시간이 오래걸리지만 empty는 생성되지 않는다.




Limitations of K-means



1) Differing Size 문제


크기가 서로 다른 클러스터를 만들어야 할 때 그렇게 하지 못하고,

큰것을 분할하는 성향이 강하다.




2) Differing Density




각각의 클러스터가 밀도가 서로 다르다면 문제가 또 발생 한다.



3) Non-Globular Shapes



구형태를 추구하는것이 K-means Algorithm의 특징이다. 

하지만 구형태가 아니라면 문제가 발생 한다. 


극복방법: 많이 쪼개서 합치는 방법이다. 아래와 같이








Hierarchical Clustering



중첩된 클러스터들로 구성된 계층적인 트리를 생성 한다.

시각적으로 볼 수 있는 특징이 있다.


생성하면 아래와 같은 모습을 볼 수 있다.

이러한 시각적인 모습을 통해서 적절히 클러스터를 생성 할 수 있다.

따라서 K를 설정할 필요가 없게 된다.


2가지 종류의 전략


Agglomerative:

Start with the points as individual clusters

At each step, merge the closest pair of clusters until only one cluster (or k clusters) left

단, 하나의 cluster가 남을 때 까지 이 방법을 반복 한다.


Divisive:

Start with one, all-inclusive cluster

At each step, split a cluster until each cluster contains a point (or there are k clusters)



Distance 또는 similarity matrix를 사용해서 clustering 과정을 수행 하게 된다.




클러스터를 합칠 때 어떻게 클러스터들간의 Similarity를 계산 할 수 있을까?


  • MIN

  • MAX

  • Group Average

  • Distance Between Centroids

  • Other methods driven by an objective function

object function means that an measure such as SSE



각각의 방법을 그림으로 표현하면 아래와 같다.





예제


Min (single llinkage)으로 계산해 보자.


5개의 point data에 대해서 similarity를 모두 계산한다.

min이니 가장 가까운 것을 합치게 된다.

similarity 가 1이면 가장 높은것을 의미 한다.


1 v 2 : 3 (0.1 or 0.7)

1 v 2 : 4 (0.65 or 0.6)

1 v 2 : 5 (0.2 or 0.5)



 

1 v 2

 1 v 2

 1

0.7

0.65 

0.5 

 3

0.7 

 1

0.4

0.3 

 4

0.65 

 0.4

0.8 

 5

 0.5

 0.3

 0.8



1 v 2 : 4 v 5 (0.65 or 0.5)

1,2,4,5의 경우 조합의 수는 총 4가지 이다. 왜 이것을 고려 하지 않을까? 이유는 둘중 가장 Similarity가 큰 것을 골랐기 때문에 나온 결과 2개중 하나만 고르게되면 이미 그것이 Similarity가 가장 큰것이 된다.


3 : 4 v 5 (0.4 or 0.3)

4 v 5 4 v 5 (0.8 or 1)


 

1 v 2

4 v 5

 1 v 2

 1

0.7

0.65 

 3

0.7 

 1

0.4

 4 v 5

0.65 

 0.4




 

1 v 2 v 3

4 v 5

 1 v 2 v 3

 1

0.65 

 4 v 5

0.65 





 

1 v 2 v 3 v 4 v 5

 1 v 2 v 3 v 4 v 5

 1



최종적으로 Min 방식인 둘 사이의 유사도가 가장 큰것을 합치는 방식으로 했을 때의

계층적 트리는 아래와 같다.




Max(Complete linkage) 방식
두개의 similarity 중에서 더 유사도가 적은것 즉 값이 작은것을 선택하는 방식이다.
하지만, 항상 병합은 가장 가까운 것들 끼리 한다는것을 염두 해야한다.
선택이 필요할때는 덜 유사한 거리가 가장 먼 것을 선택하지만 일단 병합은 가장 가까운 것들 끼리 하게 된다.



 

1 v 2

3

 1 v 2

 1 0.1 0.6 0.2

3

 0.1 1 0.4 0.3

 4

 0.6 0.4 1 0.8

 5

 0.2 0.3 0.81


 

1 v 2 

3

4v5 

 1 v 2 

 1

 0.1

 0.2

3

 0.1

 1

 0.3

 4 v 5

 0.2 0.3 1


  1v2

 3v4v5

 1v2 1 0.1
 3v4v5 0.11



Group average 방식



Clustering의 각각의 특징

MIN 방식의 강점과 약점
강점: 구 모양이 아닌것에 강하다.
단점: noise에 민감하다.


MAX 방식의 강점과 약점

강점: noise에 덜 민감하다.

단점: 큰 클러스터를 쪼개는 성향이 있다. 구 모양을 선호하는 경향이 있다.





Group Average

아래와 같은 데이터가 있을때






그룹 방식의 경우 위와 같이 계산 된다.

이중에서 결국 0.26이 가장 distance 값이 작기 때문에 이렇게 합쳐지게 된다.


장점: noise와 outlier에 민감하지 않다.
단점: 구형태의 구조를 지향한다.




계층적 클러스터의 시간과 공간 관점에서의 요구사항


O(N^2)의 공간 복잡도를 가진다. proximity matrix을 생성해야 하기 때문이다.

O(N^3) 시간 복잡도를 가진다. 많은 경우에 있어서
일단 n번 수행 해야 된다.
왜냐하면 하나하나 합쳐야하니 n번 수행 해야한다.

그다음 n^2의 매트릭을 매번 업데이트 해야 한다.
사실 이건 매트릭이 매번 줄어 들기 때문에 실제로는 N^2까지는 아니다.
upper limitation의 개념이라고 할 수 있다.

추가로 특별한 자료구조를 사용할 경우 복잡도는 더 줄어 든다.





계층적 클러스터의 문제점과 한계


계산과 공간 면에서 비싸다.


한번 결정한것을 되돌리기 어렵다.

노이즈와 아웃라이어에 민감하다.

큰 클러스터를 쪼개는 경향이 있다.


그래서 결국 어느정도 계층적 알고리즘을 초반에 돌리고 그다음 부터는

K-means로 돌리는것이 좋을 수 있다.

하나만 절대적으로 쓰지않고 복합적으로 쓰는 방법이다.







Divisive Hierarchical Clustering

큰것에서 작은것으로 쪼개는 방식이다.


MST를 생성한다음 쪼개면서 수행하게 된다.



Density-based Clustering 

밀도가 가장 낮은 영역에 의해서 두개의 밀도가 높은 영역으로 분할되는 클러스터링을 말한다.


DBSCAN 이 대표적인 알고리즘이다.

Density는 EPS즉 어떤 radius 안에 있는 point들의 수를 말한다.

core point를 결정하는 것이 중요한데

이것은 EPS 안에 특정 최소한의 포인트의 개수보다 더 높은 포인트들이 있을때 이것을 결정하는 EPS를 코어라 한다.


border point는 특정 포인트 개수 보다는 적지만 코어 포인트의 이웃일때를 의미한다.

noise 포인트의 경우 코어도 아니고 border도 아닐 때를 의미 한다.



Eps =1 

MinPts = 4 

일때의 값일때의 DBSCAN의 모습이다.




DBSCAN Algorithm

모든 포인트들이 결국 core, border, noise 이 셋중 하나로 라벨링 됨.
noise point들이 제거 된다.
2개의 코어 포인트 사이에 edge를 생성 하게 된다.
연결된 코어 포이느트들의 그룹을 생성한다 어떤 분할된 클러스터 안에
각각의 border point를 연관된 코어 포인터의 하나의 클러스터로 연결 시킨다.






노이즈에 강건 하다.

서로 다른 모양과 크기의 클러스터들을 핸들 할 수 있다.


단점

밀도가 서로 다를 경우

고차원 데이터 ( 고차원 데이터는 밀도가 낮아지는 현상)




Cluster는 어떻게 평가 할 수 있을까?

Numerical measure는 다음과 같은 세가지 타입이 존재 한다.

External Index:
    클래스 라벨이 존재하는것으로 테스트 한다.


Internal Index:

외부 데이터 없이 goodness를 측정하는 방법이다.

Sum of Squared Error (SSE)


Relative Index:

External과 Internal 방법을 모두 사용 한다.















'AI > Theory' 카테고리의 다른 글

순차데이터 분석을 위한 Hidden Markov Model (HMM)  (0) 2015.06.08
Anomaly Detection  (0) 2015.06.07
Deep Learning  (0) 2015.06.03
Association Analysis Basic Concepts and Algorithms  (0) 2015.04.27
Bayesian Classifiers  (0) 2015.04.23

Deep Learning 공부 자료


Deep Learning at NAVER

Naver Deview 강연

Imagenet contest
매년 정해진 이미지를 가지고 얼마나 더 잘 분류하는지를 경쟁하는 대회이다.
이 대회에서 1990년대 이미 흥망성쇠를 경험 했던 Deep neural network이 다시 엄청난 성능을 나타내며
부활하자 사람들이 Deep neural network으로 돌아오게 됬다.

Back propagation.
미분값을 전이하게 된다.
결국 layer가 뒤로 전이 되면서 weight vector를 찾아 내기 때문에
초기의 작은 에러가 큰 에러를 생성할 수 있다.
결국 레이어가 깊어지면 학습하기 어렵다. 작은 변화도 크게 작용하기 때문이다.
아무렇게나 막 쌓는다고해서 동작하는게 아니다.

딮 러닝의 키는 데이터인데
결국 서비스가 막강해야 데이터를 많이 모으고 그래야 또 서비스가 좋아진다.
학습 Data를 그대로 모사하게 된다. 
overfitting의 문제: 학습 데이터를 그대로 모사하게 된다.
데이터가 적을때는 오버피팅 문제가 바로 발생한다.

결국이 overfitting이 DeepLearning의 최대 장점이자 최대 단점이다.
양날의 칼날이다. 데이터가 많으면 최대의 장점이고 적으면 크리티컬한 단점이다.
노이즈를 추가해서 본질적인 부분이 계속 떠오르도록 할 수도 있다.
이말의 의미는 결국 고양이를 학습할떄 좌우 반전을 하던가 고양이 발과 같은 것들을 일부러 가려주는 방식으로
오버피팅이 잃어나지 않도록 하는 것이다.
학습이 좀더 고양이 본질에 다가설수 있도록 한다.
그렇다고 고양이 얼굴을 가려버리면 안된다.

해당 Domain에 대한 어느정도 학습을 수행하는 사람의 지식이 필요하다고 할 수 있다.

이전에는 오버피팅을 막기위해서
priori 지식이 필요했다.
이래야 됬었다.

하지만 요즘은 데이터가 많으니
필터조차 학습을 통해서 만들어 내게 된다.

예전에는 피터 익스트렉션을 사람이 해줬다.
하지만 필터를 이제 기계가 만들어준다.

그럼 아래와 같은 그림 처럼
예전에 인간이 만들어준 prior knowledge 보다 더 좋은 성능을 Big-data를 통해서 달성 할 수 있게 된다.

그럼 무작정 Data가 많으면 되는 것인가?
아무리 빅 데이터라도 분명히 커버하지 못하는 부분이 존재 한다. 따라서 그러한 부분에 대해서는 Prior knowledge를 통해서 커버를 해야한다. 하지만 빅 데이터가 커버하지 못하는 영역이므로 찾아내기란 그렇게 쉽지는 않다.

Big Data

기본적으로 Neural network에서 사용하는 Data는 supervised learning의 데이터이다.
따라서 정답이 존재하는 Data를 사용해야 한다.

정답이 있는 데이터를 모으기란 너무 어렵다.

흔이 쓰는 데크닉이 크라우드 소싱이다.
캡차 같은것이 그것이다.

semi-supervised learning
클러스터링 한다음 그다음 지도학습

스타트업에서의 Deep learning을 하기 위한것

데이터가 적은 상황에서의 딥러닝
일단 만들고 beta 서비스를 통해서 데이터를 모아야한다.

Transfer learning
지식의 전이

데이터를 공유한다.
한국어 학습 데이터를
일본어 학습 데이터로 활용 한다.

비슷한 도메인을 재활용 한다.

Speed (hardware)

모든 weight가 다 필요하지는 않다.

차원의 감소를 수행 한다.

다 필요하지만, 모든것이 필요하지는 않다 항상

GPU, CuNet (Nvidia)에서 개발됨
딮러닝이 될 수 있다.
몇줄만 써도 할 수 있다.
툴로써는 쓰기 쉽게 되어있다.

Deep Learning이 최종 솔루션인가?

이제 시작이다.
아직 인간의 뉴런갯수와 비교하면 한참 멀었다.

하지만 좋은것은 이제 시작인것에도 불구하고 성능이 다른 Machine Learning 알고리즘들을 압도하고 있다.

Deep Mind

https://www.youtube.com/watch?v=EfGD2qveGdQ

nature letter. Human-level control through deep reinforcement learning
해당 논문은 Deep neural network과 reinforcement learning algorithm을 적용함.

2014년 1월에 650만 달러에 구글에 DeepMind는 인수 합병댐

Deep Learning 자료

쉽게 풀어쓴 딥 러닝의 모든 것
http://slownews.kr/41461

구글 - Deep mind 인수
페이스북 - 뉴욕대학 얀 러쿤(Yann LeCun) 교수
바이두 - 스탠포드 앤드류 응(Andrew Ng) 교수

캐나다 토론도 대학: 제프리 힌튼 (Geoffrey Hinton) 교수 (Deep Learning의 1등 공신)

Andrew Ng (사이트)
http://ufldl.stanford.edu/wiki/index.php/UFLDL_Tutorial

Deep Learning (python site)
http://deeplearning.net/

머신러닝 기초 정리잘된 블로그
http://www.whydsp.org/237

관련기사 스크랩

인공지능과 딥러닝 8-1: 사람의 뇌에 '구글'을 이식하다
일자리 둘러싼 인간과 컴퓨터의 생존경쟁
인공지능과 딥러닝 빅데이터 안고 부활하다.

MOOC 자료

Google Launches Deep Learning with TensorFlow MOOC (Udacity)
Udacity 강의
해당 강의는

강사는 Vincent Vanhoucke로 Google’s deep learning infrastructure team 팀의 책임연구원이다.
박사학위를 스탠포드에서 받았다.
이력: http://research.google.com/pubs/VincentVanhoucke.html

Google Research Blog 공식 발표:
http://googleresearch.blogspot.kr/2016/01/teach-yourself-deep-learning-with.html

With interest in both deep learning and TensorFlow reaching remarkable heights, Google has just announced a new Deep Learning Course developed in partnership with Udacity.

The course is made up of four lectures, covering the following:

  • Lecture 1 - Focuses on machine learning basics, including setting up data and experiments, and training simple classification models.

  • Lecture 2 - Builds on the fundamentals, exploring how to make models deeper, and exploring scalability issues such as hyperparameter tuning and regularization.

  • Lecture 3 - All about convolutional networks and image recognition.

  • Lecture 4 - Explores models for text and sequences in general, with embeddings and recurrent neural networks.


'AI > Theory' 카테고리의 다른 글

Anomaly Detection  (0) 2015.06.07
Unsupervised Learning: Cluster Analysis  (3) 2015.06.03
Association Analysis Basic Concepts and Algorithms  (0) 2015.04.27
Bayesian Classifiers  (0) 2015.04.23
K Nearest Neighbors - Classification  (0) 2015.04.23

+ Recent posts