SMOTE 알고리즘은 크게 오버 샘플링, 언더 샘플링, 취합 순으로 진행된다.

오버 샘플링 : 작은 표본을 크게 복원 추출하는 방법
언더 샘플링 : 큰 표본을 작게 복원 추출 하는 방법

오버 샘플링

문자형이나 범주형 자료를 KNN에 사용하기위해 숫자형으로 변경

빈도가 가장 작았던 자료의 관측치수( rare : 50 -> nT)

300 개에 대해서 관측치별로 KNN 진행 #default 옵션은 5개 분류로 진행

자료를 6배 해야하므로 KNN 5개중 랜덤하게 1개를 뽑고 I 번째 값과의 차이(difs)에 랜덤균등분포(min=0, max=1)을 뽑아서 I 번째 자료에 더하여 자료를 생성하는 작업을 6번 함.

이렇게 300개의 빈도가 가장 작았던 자료를 KNN기반으로 오버샘플링을 진행

언더 샘플링

빈도가 제일 작은 범주를 제외한 자료를 중복을 포함하여 언더샘플링 진행

최종 출력

코드를 뜯어 본 결과 빈도가 낮은 경우가 많은 다범주에서 비율을 맞추기에는 다소 부적합해보인다.

아래는 참고한 R 코드다.

data(iris)
data <- iris[, c(1, 2, 5)]
data$Species <- factor(
                  ifelse(data$Species == "setosa","rare","common"))

## checking the class distribution of this artificial data set
table(data$Species)

## now using SMOTE to create a more "balanced problem"
newData <- SMOTE(Species ~ ., data, perc.over = 600,perc.under=100)
table(newData$Species)

"위 코드 실행 코드"
#옵션 설정
form=formula('Species ~ .');data=data;perc.over = 600; perc.under = 100;
k = 5;learner = NULL#default

#target colum을 맨 마지막으로 정렬
  ## target column 위치(Species의 컬럼 위치 ==3)
  tgt <- which(names(data) == as.character(form[[2]]))

  ## 빈도가 가장 작은 범주
  minCl <- levels(data[, tgt])[which.min(table(data[, tgt]))]

  ## 빈도가 가장 작은 범주의 행의 index
  minExs <- which(data[, tgt] == minCl)

  ## target colum의 위치가 마지막 컬럼이 아니면 마지막으로 위치 변경
  if (tgt < ncol(data)) {
    cols <- 1:ncol(data)
    cols[c(tgt, ncol(data))] <- cols[c(ncol(data), tgt)]
    data <- data[, cols]
  }# newExs <- DMwR::smote.exs(data[minExs, ], ncol(data), perc.over, k)
  Idata=data[minExs, ];tgt=ncol(data);N=perc.over;k=k

# INPUTS:
#비율이 가장 작은 변수 자료를 넣어서
# data are the rare cases (the minority "class" cases)
# tgt is the name of the target variable
# N is the percentage of over-sampling to carry out;
# and k is the number of nearest neighours to use for the generation

# OUTPUTS:
# The result of the function is a (N/100)*T set of generated
# examples with rare values on the target
# 문자열이나 범주형 자료를 KNN을 사용하기위해 숫자로 변경
nomatr <- c()

# 빈도가 가장 작은 범주 table 이 n by m 이라면 NA로 된 n by m-1의 행렬 T 생성
T <- matrix(nrow=dim(Idata)[1],ncol=dim(Idata)[2]-1)
for(col in seq.int(dim(T)[2])){
  if (class(Idata[,col]) %in% c('factor','character')) {
    T[,col] <- as.integer(Idata[,col])
    nomatr <- c(nomatr,col)
  } else T[,col] <- Idata[,col]
}

# over sample의 비율인 N이 100보다 작다면 케이스를 줄여라# 본 분석에는 600이라 해당 없음

# 만약 50이였다면 N=50 (50/100)*150 150 개의 절반인 75개를 추출
if (N < 100) { # only a percentage of the T cases will be SMOTEd
  nT <- NROW(T)
  idx <- sample(1:nT,as.integer((N/100)*nT))
  T <- T[idx,]
  N <- 100
}p <- dim(T)[2]
nT <- dim(T)[1]
ranges <- apply(T,2,max)-apply(T,2,min)
#6 배로 sampling하게 조절 nrow=900
nexs <- as.integer(N/100) # this is the number of artificial exs generated

# for each member of T
new <- matrix(nrow=nexs*nT,ncol=p) # the new cases
for(i in 1:nT) {
# the k NNs of case T[i,]
  xd <- scale(T,T[i,],ranges) #min max scaling
  for(a in nomatr){
    xd[,a] <- xd[,a]==0
  }
  #distance
  dd <- drop(xd^2 %*% rep(1, ncol(xd)))
  #현재 i 번째 값과 가까운 5개 knn
  kNNs <- order(dd)[2:(k+1)]
  #6 배 할 것이므로
  for(n in 1:nexs) {
    # select randomly one of the k NNs
    neig <- sample(1:k,1)
    ex <- vector(length=ncol(T))
    # the attribute values of the generated case
    # 해당값과 샘플링한 가까운 값의 차이를 컬럼별로 계산
    difs <- T[kNNs[neig],]-T[i,]
    #최소가 0 최대가 1인 균등분포 1개를 곱함 - 화이트노이즈의 개념인 듯
    new[(i-1)*nexs+n,] <- T[i,]+runif(1)*difs
    for(a in nomatr){
      new[(i-1)*nexs+n,a] <- c(
        T[kNNs[neig],a],T[i,a])[1+round(runif(1),0)]
    }
  }
}#새로운 데이터를 통해 900 개 생성됨

newCases <- data.frame(new)
for(a in nomatr){
  newCases[,a] <- factor(newCases[,a],
    levels=1:nlevels(Idata[,a]),labels=levels(Idata[,a]))
}

newCases[,tgt] <- factor(rep(Idata[1,tgt],nrow(newCases)),
    levels=levels(Idata[,tgt]))
colnames(newCases) <- colnames(Idata)
}#return newCases
newExs=newCases

if (tgt < ncol(data)) {
  newExs <- newExs[, cols]
  data <- data[, cols]
}

#빈도가 제일 작았던 범주를 제외한 자료를 중복허용해서 언더셈플링 비율*아까 오버샘플링 row

selMaj <- base::sample(x=(1:NROW(data))[-minExs],
size=as.integer((perc.under/100) * nrow(newExs)), replace = TRUE)
newdataset <- rbind(data[selMaj, ], data[minExs, ], newExs)

'언더샘플링/100* 오버샘플링 관측치 수=900'
'비율이 제일 작았던 범주 관측치 수 50'
'비율이 가장 작았던 자료를 KNN기반 오버샘플링 한 자료=900'
#1850
if (is.null(learner))
return(newdataset)
else do.call(learner, list(form, newdataset, ...))
}#######