for textmining

통계 기반 감성사전 구축

|

이번 글에서는 통계 기반 감성사전 구축 방법에 대해 살펴보도록 하겠습니다. 이 글은 고려대 강필성 교수님 강의와 Hur et al.(2016)을 정리하였음을 먼저 밝힙니다. 그럼 시작하겠습니다.

모델의 가정

이 모델은 평점 정보가 있는 영화 리뷰를 대상으로 합니다. 가정은 이렇습니다. 긍정적인 어휘가 쓰인 리뷰의 평점 분포는 전체 분포보다 오른쪽에 있을 것이고, 반대로 부정적인 어휘는 왼쪽에 있을 것이라는 겁니다. 이를 그림으로 나타내면 다음과 같습니다.

이 모델은 각 단어별 평점의 분포가 t분포를 따를 것이라고 가정합니다. 다음과 같이 t-test를 실시하여 검정통계량이 일정 수치를 넘으면 해당 단어를 긍정 범주, 일정 수치보다 작으면 부정 범주로 할당하게 됩니다.

$H_0$ : 전체 평균과 해당 단어의 평균 평점이 동일하다.

$H_1$ : 전체 평균과 해당 단어의 평균 평점이 같지 않다.

검정통계량

아래 식에서 $w_q$는 영화 리뷰 말뭉치에 $q$번째로 등장한 단어, $r_{i,j}$는 $i$번째 사용자가 $j$번째로 작성한 리뷰의 평점, $R(r_{i,j},w_q)$는 $i$번째 사용자가 $j$번째로 작성한 리뷰에 $q$번째 단어가 쓰였을 경우 해당 리뷰의 평점($r_{i,j}$)을 가리킵니다. 만약 해당 리뷰에 $w_q$가 포함돼 있지 않을 경우 $R(r_{i,j},w_q)$은 0이 됩니다. 한편 $m$은 전체 사용자 수, $n_i$는 $i$번째 사용자가 작성한 리뷰의 총수, $n(w_q)$는 $w_q$의 빈도수를 뜻합니다.

[\begin{align} Score({ w }_{ q })=E({ w }_{ q })&=\frac { 1 }{ n({ w }_{ q }) } \sum _{ i=1 }^{ m }{ \sum _{ j=1 }^{ { n }_{ i } }{ R({ r }_{ i,j },{ w }_{ q }) } }\Var({ w }_{ q })&=\frac { 1 }{ n({ w }_{ q })-1 } \sum _{ i=1 }^{ m }{ \sum _{ j=1 }^{ { n }_{ i } }{ { \left{ R({ r }_{ i,j },{ w }_{ q })-Score({ w }_{ q }) \right} }^{ 2 } } } \end{align}]

가설검정을 위한 검정통계량 $T_w$와 t분포의 자유도 $v$는 다음과 같습니다. $W$는 전체 단어, $w$는 가설검정 대상이 되는 개별 단어를 가리킵니다. $s^2_w$는 $Var(w)$, $E(w)$는 $Score(w)$를 뜻합니다.

[\begin{align} { T }_{ w }&=\frac { E(W)-E(w) }{ \sqrt { \frac { { s }_{ W }^{ 2 } }{ n(W) } +\frac { { s }_{ w }^{ 2 } }{ n(w) } } } \ v&=\frac { { \left{ { s }_{ W }^{ 2 }/n(W)+{ s }_{ w }^{ 2 }/n(w) \right} }^{ 2 } }{ \frac { { \left{ { s }_{ W }^{ 2 }/n(W) \right} }^{ 2 } }{ n(W)-1 } +\frac { { \left{ { s }_{ w }^{ 2 }/n(w) \right} }^{ 2 } }{ n(w)-1 } } \end{align}]

가설검정

$w$에 대한 검정통계량 $T_w$와 자유도 $v$가 주어졌을 때 가설검정은 다음과 같이 실시합니다. (유의수준=$α$)

[Positive\quad if\quad { T }{ w }>t(\alpha ,v)\ Negative\quad if\quad { T }{ w }<t(\alpha ,v)\ Neutral\quad if\quad otherwise]

실험결과

Hur et al.(2016)의 실험 결과 일부는 다음과 같습니다. 통계 기반의 기법으로도 감성 어휘를 골라내는 데 좋은 성능을 나타냄을 알 수 있습니다.

Comment  Read more

Recursive Neural Tensor Network

|

이번 글에서는 Recursive Neural Tensor Network(RNTN)에 대해 살펴보도록 하겠습니다. 이번 글 역시 고려대 강필성 교수님 강의를 정리하였음을 먼저 밝힙니다. 그럼 시작하겠습니다.

문제 및 데이터

RNTN은 Recursive Neural Networks의 발전된 형태로 Socher et al.(2013)이 제안한 모델입니다. RNTN의 입력값은 다음과 같이 문장이 단어, 구(phrase) 단위로 파싱(parsing)되어 있고 단어마다 긍정, 부정 극성(polarity)이 태깅돼 있는 형태입니다. 극성이라는 레이블 정보가 주어져 있는 파싱 트리를 기반으로 단어나 구를 벡터로 임베딩하고, 이 벡터를 기반으로 레이블을 예측하는 모델이 바로 RNTN입니다.

Simple Recursive Neural Network

RNTN 아키텍처를 살펴보려면 간단한 형태의 Recursive Neural Network를 먼저 살펴보는 것이 좋겠습니다. 아키텍처를 단순화한 그림은 다음과 같습니다. ‘very good’처럼 문법적, 의미적 응집성이 높은 입력값을 트리 형태로 결합하는 것이 Recursive Neural Network의 특징입니다.

Recursive Neural Network는 left child noderight child node를 결합해 parent node를 만듭니다. 위 예시 그림 기준으로는 very와 good을 결합해 $p_1$을 만들고, 다시 not과 $p_1$을 결합해 $p_2$를 만드는 방식입니다. Recursive Neural Network의 순전파(feedforward propagation) 수식은 다음과 같습니다.

[{ p }{ 1 }=g\left( W\begin{bmatrix} b \ c \end{bmatrix} \right) ,\quad { p }{ 2 }=g\left( W\begin{bmatrix} b \ { p }_{ 1 } \end{bmatrix} \right)]

$[b,c]^T$는 $b$라는 left child node와 $c$라는 right child node를 위, 아래로 붙인(concatenate) 벡터로 left, right child node가 $R^d$차원의 벡터라면 이 둘이 합쳐진 $[b,c]^T$는 $R^{2d}$차원 벡터가 됩니다. 한편 parent node는 $R^d$차원이 되는데요. parent node는 $p_1$처럼 또 다른 child node가 될 수 있기 때문입니다.

$g$는 보통 하이퍼볼릭탄젠트를 쓰는데 비선형성(non-linearity)을 확보할 수 있는 다른 종류의 함수를 쓸 수도 있다고 합니다. $W$는 $d$ x $2d$ 크기의 행렬로 단어나 node 종류에 관계 없이 그 값을 공유합니다.

Matrix-Vector RNN

Matrix-Vector Recursive Neural Network(MV-RNN)은 simple RNN과 달리 결합하는 단어가 다르면 그 결합 과정 또한 다를 것이라는 전제가 깔려 있는 기법입니다. 그도 그럴 것이 개별 단어는 문법적, 어휘적 성질이 각기 다르고 결합시 구성요소가 다르면 그 합성물 또한 합성의 화학구조가 달라질 것이기 때문에 이러한 특성을 반영하는 것이 어쩌면 당연할 수 있겠습니다. MV-RNN을 단순화한 그림은 다음과 같습니다.

위 그림에서 $b$는 left child node의 $R^d$차원 단어 벡터입니다. $B$는 이 벡터에 해당하는 $R^{d×d}$ 크기의 행렬입니다. 예컨대 simple RNN 예시 그림 기준으로 보면 $b$는 very에 해당하는 2차원 벡터, $B$는 이 벡터에 해당하는 행렬이 됩니다. MV-RNN에서는 모든 단어, 모든 parent node에 대해 이러한 행렬을 설정해 둡니다. 다시 말해 $p_1$은 $b$와 $c$로 만들어진 parent node라 할 때 $p_1$에 해당하는 행렬 $P_1$도 만든다는 얘기입니다.

MV-RNN의 순전파 수식은 다음과 같습니다.

[{ p }{ 1 }=g\left( W\begin{bmatrix} Cb \ Bc \end{bmatrix} \right) ,\quad P{ 1 }=g\left( { W }_{ M }\begin{bmatrix} B \ C \end{bmatrix} \right)]

parent node vector $p_1$을 만드는 과정 먼저 보겠습니다. 우선 left child에 해당하는 단어벡터 $b$에 right child의 행렬 $C$를 내적합니다. right child에 해당하는 단어벡터 $c$에 left child의 행렬 $B$를 내적합니다. 이 결과물에 parent node vector로 합쳐주는 역할을 하는 $W$를 내적하고 비선형성을 획득하는 과정은 simple RNN과 동일합니다.

이번엔 parent node matrix $P_1$을 만드는 과정입니다. left child의 행렬 $B$에 right chlid의 행렬 $C$를 합친 후 parent node matrix로 합쳐주는 역할을 하는 $W_M$를 내적하고 비선형성을 부여한 뒤 $P_1$을 생성합니다.

그러면 각 요소별 차원수를 살펴보겠습니다. parent node vector $p_1$는 $R^d$ 차원의 벡터, $W$는 $R^{d×2d}$ 크기의 행렬입니다. parent node matrix $P_1$은 $R^{d×d}$ 크기의 행렬이며, $W_M$은 $R^{d×2d}$ 크기의 행렬입니다.

RNTN

RNTN은 MV-RNN의 계산복잡성을 완화하기 위해 제안된 기법입니다. MV-RNN은 말뭉치에 등장하는 모든 단어와 결합 가능한 모든 경우의 수에 해당하는 parent node에 대해 $R^{d×d}$ 크기의 행렬을 생성하기 때문에 꽤 비용이 높은 알고리즘입니다. 이를 개선하기 위해 RNTN은 Tensor 개념을 도입합니다. 이를 단순화한 그림은 다음과 같습니다.

RNTN은 모든 단어와 parent node에 대해 relational matrix를 생성하는 대신 모든 단어와 parent node가 공유하는 Tensor를 설정하는데요. 위 그림에서 $V$가 Tensor를 의미합니다. 그림을 보시면 이 텐서는 2층짜리 행렬이라고 보시면 됩니다. 위, 아래가 겹친 형태라 어쩔 수 없이 둘로 나누어 그려진 걸 확인할 수 있습니다. RNTN의 순전파 수식은 다음과 같습니다.

[p=g\left( { \begin{bmatrix} b \ c \end{bmatrix} }^{ T }{ V }^{ [1:d] }\begin{bmatrix} b \ c \end{bmatrix}+W\begin{bmatrix} b \ c \end{bmatrix} \right)]

이해를 돕기 위해 1층 텐서만 살펴보겠습니다. 그림을 보시면 left child node에 해당하는 단어벡터 $b$와 right child node의 $c$의 차원수는 $R^d$(=2차원)입니다. 1층 텐서의 차원수는 $R^{2d×2d}$입니다. 따라서 Slice of Tensor Layer 가운데 1층의 결과물은 스칼라값 하나가 나오게 됩니다($[b,c]∈R^{1×2d}$, $V∈R^{2d×2d}$, $[b,c]^T∈R^{2d×1}$).

이를 확장해 텐서가 $d$층이라고 가정하면 Slice of Tensor Layer를 계산한 결과는 $R^d$차원의 벡터가 됩니다. 이는 $W$에 $[b,c]^T$를 내적한 Standard Layer의 차원수와 같습니다.

Softmax layer

앞서 데이터를 설명해드렸던 것처럼 입력문장엔 모든 단어, 구에 극성 레이블이 태깅돼 있습니다. 단어, 구 단위 분류 문제를 푸는 것과 같다는 얘기입니다. 예컨대 레이블 범주가 긍정, 부정, 중립 3가지라면 3범주 분류 문제가 됩니다. Simple Recursive Neural Network이든, MV-RNN이든, RNTN이든 동일한 Softmax Layer를 씁니다. 식은 다음과 같습니다.

[{ y }^{ word }=softmax({ W }_{ S }\cdot word)]

$word$는 $R^d$차원 벡터입니다. 만약 이 $word$가 말단의 child node라면 단어에 해당하는 벡터가 될 것이고 임의의 parent node라면 구로 추상화된 벡터가 될 것입니다. $W_S$의 차원수는 $R^{C×d}$가 됩니다. 여기에서 $C$는 범주 개수입니다.

위 수식의 결과물인 $y^{word}$는 $R^C$차원 벡터가 됩니다. 범주 수만큼의 확률값들이 존재하는 셈이지요. 이를 정답 레이블과 비교해 크로스 엔트로피 오차를 구하고 역전파를 수행해 전체 네트워크를 학습하는 구조입니다.

기법별 학습파라메터 비교

이번에는 기법별로 학습파라메터가 어떻게 다른지 비교해보겠습니다. MV-RNN이 학습파라메터 종류가 많고 크기가 제일 큽니다. simple RNN은 가장 적고, RNTN은 둘의 중간 정도됩니다.

구분 simple Recursive Neural Network MV-RNN RNTN
Word Vector $d×1$ $d×1$ $d×1$
Softmax $W_S$ $C×d$ $C×d$ $C×d$
Compostion Weights $W:d×2d$ $W:d×2d$, $W_M:d×2d$,$A,B,C…:d×d×$|$v$| $W:d×2d$,$V^{[1:d]}:2d×2d×d$

Comment  Read more

데이터 시각화

|

이번 글에서는 데이터 시각화 관련 코드를 정리해보겠습니다. 분석한 결과를 예쁘게 포장할 때 유용한 팁들인데요. 분석 자체에만 신경쓰다가 정작 시각화에는 실패하는 경우가 저 경험상 많았는데요. 시간도 없고 힘들어 죽겠는데 시각화 코드를 일일이 찾아 검색하는 것도 한계가 있고요. 그래서 저도 정리 겸 해서 이런 글을 쓰게 됐습니다.

출처를 일일이 밝혀야 하는데, 저도 어디서 검색했는지 가물가물한 코드가 많아서..(죄송합니다) 혹시 출처를 확실히 알고 계신 분이 있으시면 댓글로 남겨주셔요. 바로 반영하겠습니다. 단행본 ‘조엘 그루스(2015), 밑바닥부터 시작하는 데이터과학’ 등을 비롯해 다양한 자료를 참고하였음을 먼저 밝힙니다. 그럼 시작하겠습니다.

꺾은선 그래프, python

from matplotlib import pyplot as plt
years = [1950, 1960, 1970, 1980, 1990, 2000, 2010]
gdp = [300.2, 543.3, 1075.9, 2862.5, 5979.6, 10289.7, 14958.3]
plt.plot(years,gdp,color='green',marker='o',linestyle='solid')
plt.title('Nominal GDP')
plt.ylabel("Billions of $")
plt.show()

막대 그래프, python

from matplotlib import pyplot as plt
movies = ['Annie Hall','Ben-Hur','Casablanca','Gandhi','West Side Story']
num_of_oscars = [5,11,3,8,10]
# 막대 너비의 기본값이 0.8이므로 막대가 가운데로 올 수 있도록 
# 왼쪽 좌표에 0.1씩 더해줌
xs = [i + 0.1 for i, _ in enumerate(movies)]
plt.bar(xs,num_of_oscars)
plt.ylabel('# of Academy Awards')
plt.title('My Favorite Movies')
# 막대의 가운데에 오도록 영화 제목 레이블을 달자
plt.xticks([i + 0.5 for i, _ in enumerate(movies)],movies)
plt.show()

히스토그램, python

from matplotlib import pyplot as plt
from collections import Counter
grades = [83,95,91,87,70,0,85,82,100,67,73,77,0]
decile = lambda grade: grade // 10 * 10
# counter는 key와 value의 빈도를 연결시켜준다
histrogram = Counter(decile(grade) for grade in grades)
plt.bar([x-4 for x in histrogram.keys()], histrogram.values(), 8)
# X축은 -5부터 105, Y축은 0부터 5
plt.axis([-5,105,0,5])
# X축의 레이블은 0, 10, ..., 100
plt.xticks([10 * i for i in range(11)])
plt.xlabel("Decile")
plt.ylabel('# of Students')
plt.title('Distribution of Exam 1 Grades')
plt.show()

선 그래프, python

from matplotlib import pyplot as plt
variance = [1,2,4,8,16,32,64,128,256]
bias_squared = [256,128,64,32,16,8,4,2,1]
total_error = [x + y for x, y in zip(variance, bias_squared)]
xs = [i for i, _ in enumerate(variance)]
plt.plot(xs, variance, 'g-', label='variance') # 녹색 실선
plt.plot(xs, bias_squared, 'r-', label='bias^2') # 빨간 실선
plt.plot(xs, total_error, 'b:', label='total error') #  파란 점선
plt.legend(loc=9) # 범례 그리기, loc=9 ; top center를 의미
plt.xlabel('model complexity')
plt.title('The Bias-Variance Tradeoff')
plt.show()

산점도, python

from matplotlib import pyplot as plt
friends = [70,65,72,63,71,64,60,64,67]
minutes = [175,170,205,120,220,130,105,145,190]
labels = ['a','b','c','d','e','f','g','h','i']
plt.scatter(friends,minutes)
# 각 포인트에 레이블 달기
for label, friend_count, minute_count in zip(labels, friends, minutes):
    plt.annotate(label,
                 xy=(friend_count, minute_count), #label을 데이터포인트에 두되
                 xytext=(5,-5), # 약간 떨어져 있게
                 textcoords='offset points')
plt.title('Daily Minutes vs. Number of Friends')
plt.xlabel('# of friends')
plt.ylabel('daily minutes spent on the site')
plt.show

산점도, R

library(ggplot2)
pcadata <- data.frame(princomp(iris[,-5])$scores[,1:2],iris[,5])
colnames(pcadata) <- c('V1','V2','Class')
ggplot(pcadata,
       aes(x=V1,y=V2,color=Class)) + geom_point(size=1.25) + 
  guides(colour = guide_legend(override.aes = list(size=5))) + 
  xlab("") + ylab("") + ggtitle("PCA 2D Embedding of Data") + 
  theme_light(base_size=10) +
  theme(strip.background = element_blank(),
        strip.text.x     = element_blank(),
        axis.text.x      = element_blank(),
        axis.text.y      = element_blank(),
        axis.ticks       = element_blank(),
        axis.line        = element_blank(),
        panel.border    = element_blank())
ggsave('PCA.png')

heatmap, python

import matplotlib.pyplot as plt
import numpy as np
#here's our data to plot, all normal Python lists
x = [1, 2, 3, 4, 5]
y = [0.1, 0.2, 0.3, 0.4, 0.5]
intensity = [[5, 10, 15, 20, 25],
            [30, 35, 40, 45, 50],
            [55, 60, 65, 70, 75],
            [80, 85, 90, 95, 100],
    [       105, 110, 115, 120, 125]]
#setup the 2D grid with Numpy
x, y = np.meshgrid(x, y)
#convert intensity (list of lists) to a numpy array for plotting
intensity = np.array(intensity)
#now just plug the data into pcolormesh, it's that easy!
plt.pcolormesh(x, y, intensity)
plt.colorbar() #need a colorbar to show the intensity scale
plt.show() #boom

Boxplot, python

import seaborn as sns
import matplotlib.pyplot as plt
plt.figure(figsize = (35, 12))
sns.boxplot(y = 'y', x = 'x', data = data, width = 0.8, showmeans = True, fliersize = 3)
plt.show()

Joint Plot, python

import seaborn as sns
import matplotlib.pyplot as plt
plt.figure(figsize = (30, 20))
sns.jointplot(x = "x", y = "y", data = data, kind = 'scatter', size = 10)
plt.show()

Comment  Read more

데이터를 대표하는 값 찾기

|

이 글에서는 데이터를 대표하는 값을 찾는 몇 가지 방법을 소개하였습니다. 너무도 쉽고 당연해 그냥 넘길 수 있는 지표들이지만 ‘Back to the basic’의 마음가짐으로 정리해봤습니다. 이 글은 ‘밑바닥부터 시작하는 데이터과학(조엘 그루스, 인사이트 펴냄)’과 ‘일반통계학(김우철 외, 영지문화사)’ 두 책을 참고했음을 먼저 밝힙니다. 그럼 시작하겠습니다.

평균

가장 널리쓰이는 지표는 평균(mean)일 것입니다. 특성값들을 모두 더한 뒤 전체 개체 수로 나눈 산술평균으로 정의됩니다. 예컨대 다음과 같이 우리나라 15세 이상 기혼여성을 대상으로 출생아 수를 조사한 결과가 다음과 같다고 가정해봅시다. 출생아 수의 평균은 특성값과 상대도수를 곱해 모두 더한 값이 됩니다.

특성값(출생아 수) 0 1 2 3 4 5
상대도수 .058 .151 .303 .175 .113 .083

일반적으로 평균은 가느다란 막대 위해 상대도수에 비례하는 질량의 물체를 각 특성값의 위치에 놓을 때 균형점의 위치가 됩니다. 평균은 해당 집단의 균형점으로서의 균형위치를 나타냅니다.

분위수

모집단 분포의 위치를 나타내는 대표값으로서 제$p$백분위수(pth percentile)가 있습니다. 특성값을 작은 것부터 순서대로 나열하였을 때 $p$% 이상의 특성값이 그 값보다 작거나 같고, 또한 $(100-p)$%의 특성값이 그 값보다 크거나 같게 되는 값으로 정의됩니다. 특히 제25백분위수를 제1사분위수(first quartile), 제50백분위수를 중앙값(median), 제75백분위수를 제3사분위수(third quartile)이라고 하며 각각 $Q_1$, $Q_2$, $Q_3$로 표시합니다.

이 가운데 모중앙값 $Q_2$는 모평균 $μ$와 같이 모집단 분포의 중심위치를 나타내며 특성값이 연속적인 무한 모집단의 경우에는 밀도곡선의 전체 넓이를 이등분하는 점이 됩니다. 모중앙값은 추정 이론의 어려움 때문에 모평균처럼 통상적으로 고려되는 추론의 대상은 아니지만, 비모수통계학(non-parametric statistics)에선 중요 고려 대상이 됩니다. 제$p$백분위수를 파이썬 코드로 나타내면 다음과 같습니다.

def percentile(x, p):
    p_index = int(p * len(x))
    return sorted(x)[p_index]

중앙값

중앙값을 파이썬 코드로 나타내면 다음과 같습니다.

def median(v):
    n = len(v)
    sorted_v = sorted(v)
    midpoint = n // 2

    if n % 2 == 1:
        # 데이터 포인트의 개수가 홀수면 중앙값을 반환
        return sorted_v[midpoint]
    else:
        # 데이터 포인트의 개수가 짝수면 두 중앙값의 평균을 반환
        lo = midpoint - 1
        hi = midpoint
        return (sorted(v)[lo] + sorted(v)[hi]) / 2

산포

산포(dispersion)란 모집단에서 특성값이 흩어져 있는 상태를 뜻합니다. 산포에 관련된 대표적인 측도로 분산(variance)이 있습니다만 이 글 다른 꼭지에서 따로 다루겠습니다. 가장 간단한 형태의 산포 측도는 가장 큰 값과 작은 값의 차이를 나타내는 범위(range)일 겁니다. 다음과 같습니다.

def data_range(x):
    return max(x) - min(x)

사분위수 범위

산포를 나타내는 다른 지표로 사분위수범위(interquantile range)가 있습니다. 제3사분위수와 제1사분위수의 차로 정의되며, 모집단에서 가운데 50% 특성값의 범위를 나타냅니다. 몇몇 이상치가 주는 영향을 제거해 모집단의 산포를 가늠하는 데 유용한 지표로 알려져 있습니다. 사분위수범위의 파이썬 코드는 다음과 같습니다.

# 몇몇 이상치가 주는 영향을 제거해 관측치 편차 비교할 때 유용
def interquantile_range(x):
    return percentile(x, 0.75) - percentile(x, 0.25)

최빈값

데이터에 제일 자주 등장하는 특성값을 나타냅니다.

from collections import Counter
def mode(x):
    # 최빈값이 하나보다 많다면 list를 반환
    counts = Counter(x)
    max_count = max(counts.values())
    return [x_i for x_i, count in counts.items() if count == max_count]

평균을 0으로 맞추기

분산과 공분산을 구하려면 데이터의 평균을 0으로 맞추는 것이 여러모로 편리합니다. 이를 위한 함수를 정의했습니다.

def de_mean(x):
    #x의 모든 데이터 포인트에서 평균을 뺌
    n = len(x)
    x_bar = sum(x) / n
    return [x_i - x_bar for x_i in x]

분산과 표준편차

분산은 데이터의 평균과 각 특성값의 차(편차)를 제곱하여 산술평균한 것입니다. 표준편차는 분산의 제곱근입니다. 분산과 표준편차가 작을 수록 평균값에서 특성값들 사이의 거리가 가깝다는 걸 의미합니다. 이와 관련된 파이썬 코드는 다음과 같습니다.

# 분산
def variance(x):
    n = len(x)
    deviations = de_mean(x)
    sum_of_squares = sum([x_i ** 2 for x_i in deviations])
    return sum_of_squares / (n - 1)

# 표준편차
def standard_deviation(x):
    return variance(x) ** 0.5

공분산과 상관관계

상관계수(correlation coefficient)는 두 확률변수의 직선관계가 얼마나 강하고 어떤 방향인지를 나타냅니다. 상관계수에 각 확률변수의 표준편차에 해당하는 값을 곱한 것을 공분산(covariance)이라고 합니다. 파이썬 코드는 다음과 같습니다.

import numpy as np
def covariance(x, y):
    n = len(x)
    return np.dot(de_mean(x), de_mean(y)) / (n - 1)

def correlation(x, y):
    stdev_x = standard_deviation(x)
    stdev_y = standard_deviation(y)
    if stdev_x > 0 and stdev_y > 0:
        return covariance(x, y) / stdev_x / stdev_y
    else:
        return 0

Comment  Read more

이다,에 관하여

|

이번 글에서는 ‘이다’라는 형태소에 대해 살펴보겠습니다. 이번 글은 경희대 이선웅 교수님 강의와 ‘왜 다시 품사론인가(남기심 외, 커뮤니케이션북스)’에 실린 남길임(2006)을 정리했음을 먼저 밝힙니다. 그럼 시작하겠습니다.

‘이다’ : 조사? 형용사? 접사?

‘이다’는 한국어에서 명사(구)와 함께 쓰여 서술어로 기능하는 형태소의 일종입니다. 형태적으로는 자립성이 없어 앞말에 의존하여 나타나며, 앞말과의 사이에 다른 요소들의 개입을 허용하지 않습니다. 예컨대 다음과 같습니다.

나는 김철수, 나는 네 의견에는 반대, 불이야

학교문법에서는 ‘이다’를 서술격조사로 분류하고 있습니다. 조사는 주로 명사구 뒤에 나타나서 선행하는 명사구가 다른 말과 맺는 관계를 나타내거나 선행하는 명사구에 일정한 의미를 더하는 문법부류입니다. 형태적으로 변화하지 않는다는 특성을 가집니다. ‘이다’의 경우 위 예문과 같이 명사구에 결합하여 쓰이고 선행하는 말에 의존적이라는 점에서는 조사의 특성을 일부 갖고 있습니다.

하지만 ‘이다’는 아래처럼 활용한다는 특징을 가지며 그 양상은 대체로 형용사와 유사해 학교문법처럼 딱 잘라 조사라고 말하기 어렵습니다. ‘이다’의 범주를 굳이 따지자면 형용사로 분류해야 하는 것 아니냐는 이야기죠. 형용사는 형용사이되 앞말에 의존하여 나타나며 앞말과의 사이에 다른 요소들의 개입을 허용하지 않는다는 점에서 특수한 형용사(의존형용사)로 보는 견해입니다.

이다 : 학생이다, 학생이구나

형용사 : 예쁘, 예쁘구나

동사 : 먹는다, 먹는구나

하지만 형용사(어휘형태소)로 분류해야 한다는 견해에 문제가 전혀 없는 것은 아닙니다. 한국어에서 구개음화는 형태소 경계에서 /ㄷ, ㅌ/가 /$i,j$/로 시작하는 문법형태소와 연쇄하는 경우에 일어납니다. (예: 같-이 > [가치]) 다시 말해 어떤 말이 구개음화의 환경이 되는 경우, 그 말은 문법형태소일 가능성이 크다는 말입니다. ‘이다’ 앞에서도 구개음화가 일어난다는 점(예 : 밭-이다 > [바치다])을 고려할 때 ‘이다’를 문법형태소로 분류해야 한다는 견해가 설득력이 있습니다. 이와 별개로 ‘이다’의 주된 역할이 문법적 기능이라는 사실도 ‘이다’를 어휘형태소의 일종인 형용사로 분류하는 데 주저하게 만듭니다.

심지어 모음 뒤의 ‘이다’는 생략할 수 있는 경우가 많아서 분석이 매우 까다롭습니다. 없어도 되는 거라면 대체 ‘이다’는 정체가 뭐냐는 것이죠. 다음 예문을 보겠습니다.

그 문제를 푼 분은 뛰어난 {수학자이다, 수학자다}.

그 분은 뛰어난 {수학자이지만, 수학자지만} 천재는 아니다.

이 때문에 ‘이다’를 접사로 보아야 한다는 견해도 제기됩니다. 생략되거나 구개음화의 적용을 받는다는 점에서 ‘이다’가 문법형태소의 일종(통사적 접사)이 아니냐는 문제제기인 것이죠. 하지만 ‘이다’는 단어를 만드는 기능을 하는게 아니라 문장(서술어)를 만드는 기능을 한다는 점에서 이 견해도 완벽하지 않습니다.

지금까지의 논의가 ‘이다’의 성격을 완전히 설명하는 것은 아니고 각자 일부의 진실을 드러내고 있으며 그 영역이 중첩돼 있습니다. 다시 말해 ‘이다’는 학교문법의 기존 9품사 체계에서 어느 한 쪽에 분류하기 쉽지 않다는 이야기입니다. 이 글에서는 ‘이다’를 특정 문법 범주로 분류하기보다 ‘이다’에 나타나는 다양한 문법적 성격을 제가 이해하는 한도 내에서 설명해볼까 합니다.

대상의 정체 밝힘

‘이다’는 어휘적으로 명시적인 의미가 드러나지 않아 ‘이다’가 어휘 의미를 가지느냐에 대해서는 국어학계 논쟁의 대상이었습니다. 이와 관련해 외솔 최현배 선생(1894-1970)은 ‘이다’의 의미를 문장의 서술을 완전히 하는 지정의 의미를 가진다고 설명하면서 ‘잡음씨’로 규정한 바 있습니다. 이때 지정이란 대상의 정체를 밝히는 확인(identification)을 가리킵니다.

실제로 ‘이다’가 가장 많이 쓰이는 부류가 바로 지정입니다. 예문을 보겠습니다.

속성 지정 : 너는 바보

동일대상 지정 : 영희가 그녀이다

앞말과 뒷말 연결

계사(繫辭, copula)란 문장의 주어와 보어, 주어와 서술어를 연결시키는 단어를 가리킵니다. 계사의 기능 중 대표적인 것이 지정이며 영어 be동사가 대표적인 계사로 분류됩니다. 국어의 ‘이다’ 역시 몇 가지 쓰임에 있어서는 be동사와 유사한 특성을 가집니다. 아래 예문과 같습니다.

동일대상 : 나는 김철수, The Morning Star is the Evening Star

일원 : 나는 학생이다, The Moscow is a large city

속성 : 그는 12살이고 사춘기, The hen is next to the cockerel

앞말을 서술어로 만들어주기

한국어에서 명사는 의미상으로 서술어적 성격을 가지고 있다고 하더라도 어미가 결합할 수 없어서 문법적으로는 서술어로 기능할 수 없습니다. 예컨대 다음 문장과 같습니다.

체육은 진이가 최고

위 문장에서 ‘최고’는 서술어적 성격을 지니지만 문법적으로는 명사이기 때문에 완전한 문장이라고 할 수 없습니다. 이때 해당 명사가 서술어로 기능할 수 있도록 받쳐주는 버팀목 같은 역할을 하는 요소가 필요합니다. 이런 역할을 하는 요소를 기능동사라고 합니다. 다음과 같습니다.

체육은 진이가 최고.

‘이다’는 기능동사로 쓰이는 경우가 많습니다. 이때 ‘이다’는 정말 별뜻없이 서술이라는 용언 기능만을 담당할 뿐입니다. 예문을 보겠습니다.

(1) 나는 그 의견에 반대

(2) 내 연구실이 엉망이다

(3) 우리는 친구

위 예문의 (1)에서 ‘나’라는 행위주역(Agent)과 ‘그 의견’이라는 대상역(Theme)이 실현된 데에는 ‘반대’라는 서술성명사가 있었기 때문입니다. 다시 말해 ‘반대’가 ‘나’와 ‘그 의견’을 논항으로 요구했기 때문이지 ‘이다’에 특별한 의미가 있는 것이 아니라는 이야기입니다.

마찬가지로 (2)의 ‘엉망’은 ‘내 연구실’을, (3)의 ‘친구’는 ‘우리’를 논항으로 요구해 실현된 것을 확인할 수 있습니다. (1)~(3)처럼 서술성명사에 후행하는 ‘이다’는 별뜻없이 서술이라는 용언 기능만 하고 있습니다. 한편 한국어 기능동사에는 ‘이다’ 외에 ‘하다’(공부하다, 건강하다 등)도 있습니다.

이제부터는 ‘이다’를 용례별로 정리해 보겠습니다.

감탄, 놀라움, 사실의 전달

‘이다’의 다음과 같이 도입문(presentation sentence) 형식으로도 나타납니다. 주로 발화 현장에 있는 대상을 지시하는 체언 뒤에 쓰여서 현장에 있는 사실에 대한 감탄, 놀라움 등을 나타내거나 어떤 사실을 알릴 때 씁니다.

와, 눈이다

경찰이다!

손님, 주문하신 카페라떼입니다

구어에서의 다양한 쓰임

구어에서 ‘이다’는 상황, 맥락에 따라 결합제약 없이 자유롭게 쓰입니다. 예컨대 ‘나는 커피다’라는 표현은 평소엔 비문입니다. 하지만 음료를 정해야 하는 상황에서 ‘난 커피다’라고 말하는 것은 자연스럽습니다. 이때 ‘이다’의 의미는 통사론의 분석 범주를 넘어서 의미론, 화용론적 인 것이 됩니다. 그 예는 다음과 같습니다.

커피는 셀프입니다

다음 주는 한강이지?

침대는 과학입니다

관용표현

명사류와 ‘이다’가 함께 쓰여 본래 명사나 ‘이다’에서 전혀 발견할 수 없는 새로운 의미를 가지는 경우도 있습니다. 이럴 때는 구 전체를 한 단위로 보는 게 적절할 수 있습니다.

김치 냄새는 양반이지

아유, 쟤는 참 밥맛이야

돈 십만 원이 어디야?

북한은 협상에 도사다

이제부터 거짓말하지 않기다

-ㄴ/ㄹ 명사+이다

‘이다’는 다음과 같이 ‘-ㄴ/ㄹ 명사+이다’ 구성으로도 자주 쓰입니다. 이들은 주로 선행하는 절에 대한 화자에 태도, 즉 양태(modality)를 나타냅니다.

나는 다음 주 일본에 갈 것이다

내일부터 일기를 쓸 예정이야

이젠 어떻게 할 생각이니?

왠지 무슨 일이라도 일어날 것 같은 느낌이다

날 속이겠다는 작정이었군

위 용례 가운데 말뭉치에 가장 많이 나타나는 표현은 바로 ‘-ㄴ/ㄹ 것이다’입니다. 남길임(2006)에 따르면 21세기 세종계획 1000만 어절 균형말뭉치에는 ‘것이다’가 주로 아래와 같이 축약, 활용한 형태로 실현됩니다.

거야, 거거든요, 거고, 거구, 거군요, 거네, 거니, 거니까, 거다, 거라구, 거라면, 거면, 거야, 거예요, 거잖아, 거요, 거지, 건가요, 건데, 걸까, 겁니까…

‘-ㄴ/ㄹ 명사+이다’ 구성에서 명사 위치에 실현되는 어휘의 목록은 다음과 같습니다.

각오, 경향, 계획, 기분, 기색, 눈빛, 눈치, 느낌, 마음, 방침, 상황, 처지, 생각, 설명, 속셈, 시늉, 실정, 심사, 심산, 심정, 안색, 예정, 입장, 의견, 작정, 태세, 표정, 형국, 형편…

부사+이다

‘이다’는 다음과 같이 몇몇 부사와 결합하여 그 부사의 뜻을 강조할 때도 쓰입니다. 소수의 부사와만 결합하고 결합에 있어서 특별한 규칙을 찾을 수 없다는 점에서 관용적인 용법의 일종으로 볼 수 있습니다.

여름엔 이 노래가 딱이다

내일 참석해 주세요. 꼭이요!

정말이지 시댁에 너무 가기 싫습니다

‘부사+이다’ 구성에서 부사 위치에 실현되는 어휘의 목록은 다음과 같습니다

아직, 벌써, 오래, 금방, 금세, 보통, 별로, 제발, 따로, 계속, 먼저, 바로, 또, 캡, 짱, 왕, 딱, 꼭, 일쑤, 십상, 그만, 고작, 제법, 물론…

Comment  Read more