Friday, August 31, 2018

Bayesian inference (Poisson distribution ②)

This article is continuation from Bayesian inference (Poisson distribution) ①. In this article, we're gonna work on comparison between maximum likelihood estimator and predictive model of bayesian inference.

1. Maximum likelihood estimation

First of all, we try to obtain maximum likelihood estimator.

$$\begin{eqnarray}p(X|\lambda) &=& \prod^N_np(x_n|\lambda)\\ &=&\prod^N_n \frac{\lambda^x}{x_n!}e^{-\lambda}\end{eqnarray}$$

In frequetionist setting, we can estimate a value for $\lambda$ by maximizing the likelihood function. It's equivalent to maximizing the logarithm of likelihood.

$$log p(X|\lambda) = (\sum^N_n x_n)\log\lambda -N\lambda -\sum^N_n \log x_n!$$

So as to obtain maximum likelihood estimator, we can take derivative with respect to $\lambda$ equal to $zero$.

$$\begin{eqnarray}\frac{d \log p(X|\lambda)}{d\lambda} &=& \frac{\sum^N_n x_n}{\lambda} &=& 0 \\ \lambda_{ML} &=& \frac{\sum^N_n x_n}{N}\end{eqnarray}$$

It seems maximum likelihood estimator is mean of observed value. Next, we'll see implementation of maximum likelihood estimation.   
As an observed data, we will "Wine quality data set". You can refer here. Since I personally prefer red wine over white wine, red wine is gonna be used.

In [1]:
import pandas as pd
import numpy as np
from scipy.stats import gamma
from scipy.stats import poisson
from scipy.stats import nbinom
import matplotlib.pyplot as plt
% matplotlib inline
In [2]:
red_wine = pd.read_csv('./winequality/winequality-red.csv',
                       delimiter=';')
df_quility = pd.DataFrame(red_wine.quality.value_counts().sort_index())
df_quility.columns = ['number']
df_quility.index.name = ['quality']
df_quility.head()
Out[2]:
number
[quality]
3 10
4 53
5 681
6 638
7 199

Take a look at observed data with bar graph.

In [3]:
# plot observed value
plt.bar(df_quility.index,df_quility.number.values)
plt.title('Observed value',fontsize=14)
plt.xlabel('quality',fontsize=12)
plt.ylabel('occurance',fontsize=12)
plt.xlim((0,20))
plt.ylim((0,800))
plt.show()

Next we will compute the value of parameter $\lambda$ of poisson distribution with maximum likelihood estimator we calculated.

In [4]:
# create observed data from red_wine data
obs_data =[]

for qual, num in zip(df_quility.index, df_quility.number):
    temp_list = [qual for i in range(num)]
    obs_data.extend(temp_list)
    
# compute maximum likelihood estimator
ml_val = np.array(obs_data).mean()
print('Maximum likelihood result : ',ml_val)
Maximum likelihood result :  5.63602251407

Now we can see the result of maximul liklihood estimation in a graph.

In [5]:
x = np.linspace(0,20,21)
y = poisson.pmf(x,ml_val)

# plot result of maximum liklihood estimation
plt.bar(x,y)
plt.title('maximum likelihood estimation',fontsize=14)
plt.ylim((0,0.20))
plt.xlim((0,20))
plt.show()

It furnish better understanding to compare the sample from maximum likelihood estimation and observed value.

In [6]:
plt.figure(figsize=(13,5))

# plot observed value
plt.subplot(1,2,1)
plt.bar(df_quility.index,df_quility.number.values)
plt.title('Observed value',fontsize=14)
plt.xlabel('quality',fontsize=12)
plt.ylabel('occurance',fontsize=12)
plt.xlim((0,20))
plt.ylim((0,700))

# The number of observation is 1599
plt.subplot(1,2,2)
sample_ml = poisson.rvs(ml_val,size=1599)
binn = np.linspace(sample_ml.min(),sample_ml.max(),
                   sample_ml.max()-sample_ml.min() +1)
occur_ml = [(sample_ml == abinn).sum() for abinn in binn]
plt.bar(binn,occur_ml)
plt.title('Sample from maximum likelihood estimator',
          fontsize=14)
plt.xlim((0,20))
plt.ylim((0,700))
plt.show()

2. Predictive model

Next we're gonna look at predictive model with bayesian inference. Predictive model can be given as following,

$$\begin{eqnarray}p(x_*|X) &=& \int p(x_*,\lambda|X)d\lambda \\ &=& \int \frac{p(x_*|\lambda)p(X|\lambda)p(\lambda)}{p(X)}\\ &=& \int p(x_*|\lambda)p(\lambda|X)d\lambda\end{eqnarray}$$

Calculatiino process will be omitted here, as a result, predictive model can be captured as negative binomnal distribution which takes form,

$$p(x_*) = NB(x_*|r,p)\ \ where\ \ r=a,\ \ p=\frac{b}{b+1}\tag{1}$$

First of all, posterior distribution should be formed with observed data. This process was described in Bayesian inference (Poisson distribution) ①

In [7]:
x_pr = np.linspace(1,40,101)
y_pr = gamma.pdf(x_pr,a=4,scale=1/0.2)

plt.plot(x_pr,y_pr)
plt.title('prior distribution',fontsize = 15)
plt.show()
In [8]:
def train_gamma(data, hyper_a=3, hyper_b=0.2):
    """
    Return parameter of posterior distribution
    """
    hat_a = data.sum() + hyper_a
    hat_b = data.shape[0] + hyper_b
    
    return hat_a, hat_b
In [9]:
hat_a,hat_b = train_gamma(np.array(obs_data))
y_posterior = gamma.pdf(x_pr,a=hat_a,scale=1/hat_b)

# plot posterior distribution
plt.plot(x_pr,y_posterior)
plt.title('posterior distribution',fontsize=15)
plt.show()

Now we can comupte predictive model with result of posterior distribution and equality (1).

In [10]:
r = hat_a
p = hat_b/(1+ hat_b)

y_predict = nbinom.pmf(x,r,p)
plt.bar(x,y_predict)

plt.title('baysian inference',fontsize=14)
plt.ylim((0,0.20))
plt.show()

As a last, we will compare observed value and sample from maximum likelihood estimator and predictive model of baysian inference.

In [11]:
plt.figure(figsize=(13,10))

# plot observed value
plt.subplot(2,2,1)
plt.bar(df_quility.index,df_quility.number.values)
plt.title('Observed value',fontsize=14)
plt.xlabel('quality',fontsize=12)
plt.ylabel('occurance',fontsize=12)
plt.xlim((0,20))
plt.ylim((0,700))

# The number of observation is 1599
plt.subplot(2,2,2)
sample_ml = poisson.rvs(ml_val,size=1599)
binn = np.linspace(sample_ml.min(),sample_ml.max(),
                   sample_ml.max()-sample_ml.min() +1)
occur_ml = [(sample_ml == abinn).sum() for abinn in binn]
plt.bar(binn,occur_ml)
plt.title('Sample from maximum likelihood estimator',
          fontsize=14)
plt.xlim((0,20))
plt.ylim((0,700))

# The number of observation is 1599
plt.subplot(2,2,3)
sample_predict = nbinom.rvs(r,p,size=1599)
binn = np.linspace(sample_ml.min(),sample_predict.max(),
                   sample_predict.max()-sample_predict.min() +1)
occur_ml = [(sample_ml == abinn).sum() for abinn in binn]
plt.bar(binn,occur_ml)
plt.title('Sample from Predictive model of bayesian inference',
          fontsize=14)
plt.xlim((0,20))
plt.ylim((0,700))
plt.show()

Sunday, August 12, 2018

Lagrange multipliers

"The method of Lagrange multipliers" is one of indispensable theorem for my life. Therefore I will share what the "Lagrange multipliers" is and some intuitive understanding about it.

0. Lagrange multipliers technique

First of all, let's wrapp you head around lagrange multipliers technique. To make it simple, we're gonna deal with only 2 dimention in this article. Let's say you wanna maximize multivariate function $f(x,y)$. subject to the constraint that another multivariate function equals a constant, $g(x,y) = C$
In that situation we can apply lagrange multiplier technique. At first introduce new variable $\lambda$. And define new function $L$ which takes form

$$L(x,y,\lambda) = f(x,y) - \lambda \varphi(x,y)\ \ where\ \ \varphi(x, y) = g(x,y) -C$$

$L$ is called "Lagrange function", $\lambda$ is "Lagrange multiplier".
Suppose $(x_0, y_0)$ maximaize (or minimize) $f(x,y)$ subject to $\varphi(x,y) = 0$, the following equality holds,

$$\frac{\partial L}{\partial x} = \frac{\partial L}{\partial y} = \frac{\partial L}{\partial \lambda} = 0$$

1. Intuitive understanding

From above equality,
$$\begin{eqnarray}\frac{\partial \left\{f(x,y) - \lambda \varphi(x,y)\right\}}{\partial x}&=&0 \\ \frac{\partial f(x)}{\partial x} &=& \lambda \frac{\partial g(x)}{\partial x}\end{eqnarray}$$   In the same way, $$\begin{eqnarray}\frac{\partial f(x)}{\partial y} &=& \lambda \frac{\partial g(x)}{\partial y}\end{eqnarray}$$  

Therefore, $$\nabla f = \lambda \nabla \varphi$$

You can tell gradient of $f(x,y)$ and $g(x,y)$ are parallel at the point of $(x,y)$ which maximaize (or minimize) $f(x,y)$ subject to $g(x,y)$.

2. What the $\nabla f(x,y)$ is ??

Let's think about contour of $f(x,y)$ where $f(x,y) = C$. Suppose moving the point along contour by $(\varDelta x, \varDelta y)$. At the time, total derivative can be captured as below,

$$\frac{\partial f}{\partial x}dx + \frac{\partial y}{\partial y} dy = \nabla f(x) \cdot \varDelta x= 0$$

Therefore gradient $\nabla f$ is gonna be normal vector to tangent vector of contour.

Tuesday, August 7, 2018

Basic usage of CaboCha in Python

When I work on *natural language processing* of Japanese language," I believe *dependency structre analysis*" is one of the crucial approach. When it comes to Japanese language, *CaboCha* is one of predominantly used tool. Needless to say, *binding* for Python is provided. In this article, I will share some basic tips regarding the way of utilizing it.

1. fundamental usage

Fist of all, I'd like to write it down the fundamental usage of Cabocha in python. Apparently in a "CABOCHA_FORMAT_TREE" foramat, the position of 'D' indicates which chunk relates to othere chunk.

In [1]:
import CaboCha
In [2]:
# Instantiate CaboCha.Parser class
cap = CaboCha.Parser()
# Parse objective sentence
tree = cap.parse('久々に新しいmacを買った。')
In [3]:
# You can check the dependency in "CABOCHA_FORMAT_TREE".
print(tree.toString(CaboCha.CABOCHA_FORMAT_TREE))
  久々に-----D
    新しい-D |
       macを-D
      買った。
EOS

In [4]:
# You can get dependency, chunk and token in xml format.
print(tree.toString(CaboCha.CABOCHA_FORMAT_XML))
<sentence>
 <chunk id="0" link="3" rel="D" score="-1.640429" head="0" func="1">
  <tok id="0" feature="名詞,一般,*,*,*,*,久々,ヒサビサ,ヒサビサ">久々</tok>
  <tok id="1" feature="助詞,格助詞,一般,*,*,*,に,ニ,ニ">に</tok>
 </chunk>
 <chunk id="1" link="2" rel="D" score="1.466958" head="2" func="2">
  <tok id="2" feature="形容詞,自立,*,*,形容詞・イ段,基本形,新しい,アタラシイ,アタラシイ">新しい</tok>
 </chunk>
 <chunk id="2" link="3" rel="D" score="-1.640429" head="3" func="4">
  <tok id="3" feature="名詞,一般,*,*,*,*,*">mac</tok>
  <tok id="4" feature="助詞,格助詞,一般,*,*,*,を,ヲ,ヲ">を</tok>
 </chunk>
 <chunk id="3" link="-1" rel="D" score="0.000000" head="5" func="6">
  <tok id="5" feature="動詞,自立,*,*,五段・ワ行促音便,連用タ接続,買う,カッ,カッ">買っ</tok>
  <tok id="6" feature="助動詞,*,*,*,特殊・タ,基本形,た,タ,タ">た</tok>
  <tok id="7" feature="記号,句点,*,*,*,*,。,。,。">。</tok>
 </chunk>
</sentence>

The followings are the some useful attirute CaboCha offer.

In [5]:
# the number of chunk
print('The number of chunk : ', tree.chunk_size())
# the number of token
print('The number of token : ', tree.token_size())
# 3rd token in the sentence
print('3rd token in the sentence : ', tree.token(2).surface)
# 3rd chunk in the sentence
print('3rd chunk relates to ', tree.chunk(2).link)
# The number of tokens the 1st chunk includes
print('The number of tokens the 1st chunk inclued is ',
      tree.chunk(2).token_size)
The number of chunk :  4
The number of token :  8
3rd token in the sentence :  新しい
3rd chunk relates to  3
The number of tokens the 1st chunk inclued is  2

2. Useful function which retrieve dependency structure analysis

In practice, we don't wanna use attributes above each time. I'd like to share one of the example of function which extract dependency analysis from a sentence. It seems a somehow clumsy however I would be glad if you get some sence out of it.
First of all, think about extract of dependency and chunks from a sentence.

In [6]:
def dep_ana(sentence, alltoken=True):
    """
    Return the result of dependent analysis
    """
    tokens, chunks = tok_chu_ana(sentence)
    
    depend_rel = {}
    chunk_list =[]
    score_list = []
    num=0
    
    for i in range(len(chunks)):
        # store dependency
        depend_rel[i] = chunks[i].link
        score_list.append(chunks[i].score)
        temp_chunk = ''
    
        for _j in range(chunks[i].token_size):
            if tokens[num].chunk is not None or alltoken :
                temp_chunk +=tokens[num].feature.split(',')[6]
            num = num+1
        chunk_list.append(temp_chunk)
    
    return depend_rel,chunk_list,score_list
    
def tok_chu_ana(sentence):
    """
    Return tokens and chunks that sentence contains
    """
    cap = CaboCha.Parser()
    tree = cap.parse(sentence)
    
    tokens = [tree.token(i) for i in range(tree.token_size())]
    chunks = [tree.chunk(i) for i in range(tree.chunk_size())]
    
    return tokens,chunks

The following is usage of this function.

In [7]:
dependency,chunks,scores = dep_ana('久々に新しい鉛筆を買った。',
                                   alltoken=True)
print('dependency : ',dependency)
print('chunks : ',chunks)
print('score : ',scores)
dependency :  {0: 3, 1: 2, 2: 3, 3: -1}
chunks :  ['久々に', '新しい', '鉛筆を', '買うた。']
score :  [-1.6404287815093994, 1.4669578075408936, -1.6404287815093994, 0.0]

You can also get first token in each chunk by specifying 'alltoken' as False.

In [8]:
dep_ana('久々に新しい鉛筆を買った。',alltoken=False)
Out[8]:
({0: 3, 1: 2, 2: 3, 3: -1},
 ['久々', '新しい', '鉛筆', '買う'],
 [-1.6404287815093994, 1.4669578075408936, -1.6404287815093994, 0.0])

Now we'd like to obtain the result of dependency structure analysis.

In [9]:
def extract_dep(result_of_analysis,threshhold=0):
    
    # list to contain the result of dependency analysis
    depend_list = []
    
    for depends, chunks, scores in result_of_analysis:
        temp_depend = []
    
        for key,score in zip(depends.keys(), scores):
            if score >threshhold:
                temp_depend.append('{}...{}'.format(chunks[key],
                                                    chunks[depends[key]]))
        
        depend_list.append(temp_depend)
    
    return depend_list
In [10]:
sentences = ['久々に新しい鉛筆を買った。','白い猫が横切った。']
result_of_analysis = [dep_ana(sentence,alltoken=False) for 
                      sentence in sentences]
extract_dep(result_of_analysis)
Out[10]:
[['新しい...鉛筆'], ['白い...猫', '猫...横切る']]