日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程语言 > python >内容正文

python

条件随机场 python_如何直观地理解条件随机场,并通过PyTorch简单地实现

發(fā)布時(shí)間:2024/1/23 python 35 豆豆
生活随笔 收集整理的這篇文章主要介紹了 条件随机场 python_如何直观地理解条件随机场,并通过PyTorch简单地实现 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

條件隨機(jī)場是一種無向圖模型,且相對于深度網(wǎng)絡(luò)有非常多的優(yōu)勢,因此現(xiàn)在很多研究者結(jié)合條件隨機(jī)場(CRF)與深度網(wǎng)絡(luò)獲得更魯棒和可解釋的模型。本文結(jié)合 PyTorch 從基本的概率定義到模型實(shí)現(xiàn)直觀地介紹了 CRF 的基本概念,有助于讀者進(jìn)一步理解完整理論。

假設(shè)我們有兩個(gè)相同的骰子,但是其中的一個(gè)是公平的,每個(gè)點(diǎn)數(shù)出現(xiàn)的概率相同;另一個(gè)骰子則被做了手腳,數(shù)字 6 出現(xiàn)的概率為 80%,而數(shù)字 1-5 出現(xiàn)的概率都為 4%。如果我給你一個(gè) 15 次投擲骰子的序列,你能預(yù)測出我每次投擲用的是哪一枚骰子嗎?

為了得到較高的準(zhǔn)確率,一個(gè)簡單的模型是,每當(dāng)「6」出現(xiàn)的時(shí)候,我們那就預(yù)測使用了有偏的骰子,而出現(xiàn)其他數(shù)字時(shí)則預(yù)測使用了公平的骰子。實(shí)際上,如果我們在每次投擲時(shí)等可能地使用任意一個(gè)骰子,那么這個(gè)簡單的規(guī)則就是你可以做到的最好預(yù)測。

但是,設(shè)想一種情況:如果在使用了公平的骰子后,我們下一次投擲時(shí)使用有偏的骰子的概率為 90%,結(jié)果會怎樣呢?如果下一次投擲出現(xiàn)了一個(gè)「3」,上述模型會預(yù)測我們使用了公平的骰子,但是實(shí)際上我們使用有偏的骰子是一個(gè)可能性更大的選項(xiàng)。我們可以通過貝葉斯定理來進(jìn)行驗(yàn)證這個(gè)說法:

其中隨機(jī)變量 y_i 是第 i 次投擲所用的骰子類型,x_i 是第 i 次投擲得到的點(diǎn)數(shù)。

我們的結(jié)論是,在每一步中作出可能性最大的選擇只是可行策略之一,因?yàn)槲覀兺瑫r(shí)可能選擇其它的骰子。更有可能的情況是,以前對骰子的選擇情況影響了我未來會做出怎樣的選擇。為了成功地進(jìn)行預(yù)測,你將不得不考慮到每次投擲之間的相互依賴關(guān)系。

條件隨機(jī)場(CRF)是一個(gè)用于預(yù)測與輸入序列相對應(yīng)標(biāo)注序列的標(biāo)準(zhǔn)模型。目前有許多關(guān)于條件隨機(jī)場的教程,但是我所看到的教程都會陷入以下兩種情況其中之一:1)全都是理論,但沒有展示如何實(shí)現(xiàn)它們 2)為復(fù)雜的機(jī)器學(xué)習(xí)問題編寫的代碼缺少解釋,不能令讀者對代碼有直觀的理解。

之所以這些作者選擇寫出全是理論或者包含可讀性很差的代碼教程,是因?yàn)闂l件隨機(jī)場從屬于一個(gè)更廣更深的課題「概率圖模型」。所以要想深入涵蓋其理論和實(shí)現(xiàn)可能需要寫一本書,而不是一篇博文,這種情況也使得學(xué)習(xí)條件隨機(jī)場的知識比它原本所需要的更困難。

本教程的目標(biāo)是涵蓋恰到好處的理論知識,以便你能對 CRF 有一個(gè)基本的印象。此外我們還會通過一個(gè)簡單的問題向你展示如何實(shí)現(xiàn)條件隨機(jī)場,你可以在自己的筆記本電腦上復(fù)現(xiàn)它。這很可能讓你具有將這個(gè)簡單的條件隨機(jī)場示例加以改造,用于更復(fù)雜問題所需要的直觀理解。

理論

我們對于理論的討論將分為三個(gè)部分:1)指定模型參數(shù) 2)如何估計(jì)這些參數(shù) 3)利用這些參數(shù)進(jìn)行預(yù)測,這三大類適用于任何統(tǒng)計(jì)機(jī)器學(xué)習(xí)模型。因此從這個(gè)意義上說,條件隨機(jī)場并沒有什么特別的,但這并不意味著條件隨機(jī)場就和 logistic 回歸模型一樣簡單。我們會發(fā)現(xiàn),一旦我們要面對一連串的預(yù)測而不是單一的預(yù)測,事情就會變得更加復(fù)雜。

指定模型參數(shù)

在這個(gè)簡單的問題中,我們需要擔(dān)心的唯一的參數(shù)就是與從一次投擲轉(zhuǎn)換到下一次投擲狀態(tài)的分布。我們有六種狀態(tài)需要考慮,因此我們將它們存儲在一個(gè) 2*3 的「轉(zhuǎn)移矩陣」中。

第一列對應(yīng)于「從前一次投擲使用公平骰子的狀態(tài),轉(zhuǎn)換到當(dāng)前使用公平骰子狀態(tài)的概率或成本(第一行的值),或轉(zhuǎn)換到有偏骰子狀態(tài)的概率(第二行的值)」。因此,第一列中的第一個(gè)元素編碼了在給定我本次投擲使用了公平骰子的前提下,預(yù)測下一次投擲使用公平骰子的概率。如果數(shù)據(jù)顯示,我不太可能在連續(xù)使用公平骰子,模型會學(xué)習(xí)到這個(gè)概率應(yīng)該很低,反之亦然。同樣的邏輯也適用于第二列。

矩陣的第一和第二列假設(shè)我們知道在前一次投擲中使用了哪個(gè)骰子,因此我們必須將第一次投擲作為一個(gè)特例來對待。我們將把相應(yīng)的概率存儲在第三列中。

參數(shù)估計(jì)

假設(shè)給定一個(gè)投擲的集合 X* *以及它們相應(yīng)的骰子標(biāo)簽 Y。我們將會找到使整個(gè)訓(xùn)練數(shù)據(jù)的負(fù)對數(shù)似然最小的轉(zhuǎn)移矩陣 T。我將會向你展示單個(gè)骰子投擲序列的似然和負(fù)對數(shù)似然是什么樣的。為了在整個(gè)數(shù)據(jù)集上得到它,你要對所有的序列取平均。

P(x_i | y_i) 是在給定當(dāng)前的骰子標(biāo)簽的前提條件下,觀測到一個(gè)給定骰子投擲點(diǎn)數(shù)的概率。舉例而言,如果 y_i 為公平骰子,則 P(x_i | y_i) = 1/6。另一項(xiàng) T(y_i | y_{i-1}) 是從上一個(gè)骰子標(biāo)簽轉(zhuǎn)換到當(dāng)前投資標(biāo)簽的概率,我們可以直接從轉(zhuǎn)移矩陣中讀取出這個(gè)概率。

請注意在分母中,我們是怎樣在所有可能標(biāo)簽 y' 的序列上進(jìn)行求和的。在傳統(tǒng)的二分類問題 logistic 回歸中,我們在分母中會有兩個(gè)項(xiàng)。但是現(xiàn)在,我們要處理的是標(biāo)注序列,并且對于一個(gè)長度為 15 的序列來說,一共有 2^15 種可能的標(biāo)簽序列,所以分母項(xiàng)是十分巨大的。條件隨機(jī)場的「秘密武器」是,它假定當(dāng)前的骰子標(biāo)簽僅僅只取決于之前的骰子標(biāo)簽,來高效地計(jì)算這個(gè)大規(guī)模求和。

這個(gè)秘密武器被稱為「前向-后向算法」。對該算法的深入討論超出了這篇博文的范圍,因此這里不做詳細(xì)的解釋。

序列預(yù)測

一旦我們估計(jì)出了我們的轉(zhuǎn)移矩陣,我們可以使用它去找到在給定一個(gè)投擲序列的條件下,最有可能的骰子標(biāo)注序列。要做到這一點(diǎn),最簡單的方法就是計(jì)算出所有可能的序列的似然,但這即使對于中等長度的序列也是十分困難的。正如我們在參數(shù)估計(jì)中所做的那樣,我們將不得不用一種特殊的算法高效地搜索可能性最大的序列。這個(gè)算法與「向前-向后算法」很相近,它被稱為「維特比算法」。

代碼

PyTorch 是一個(gè)在 Python 語言環(huán)境下為訓(xùn)練深度學(xué)習(xí)模型而編寫的深度學(xué)習(xí)庫。盡管我們在這里并不是要進(jìn)行深度學(xué)習(xí),但 PyTorch 的自動微分庫將幫助我們通過梯度下降算法訓(xùn)練條件隨機(jī)場模型,而無需我們手動計(jì)算任何梯度。使用 PyTorch 會迫使我們實(shí)現(xiàn)「向前-向后算法」的前向部分以及「維特比算法」,這比我們直接使用專門的條件隨機(jī)場構(gòu)建的 Python 包更有指導(dǎo)意義。

首先,讓我們先設(shè)想一下結(jié)果應(yīng)該是什么樣的。我們需要一種方法去計(jì)算給定骰子標(biāo)簽時(shí),對于任意投擲序列的對數(shù)似然度,下面我們給出一種可能的方法:def neg_log_likelihood(self, rolls, states):

"""Compute neg log-likelihood for a given sequence.

Input:

rolls: numpy array, dim [1, n_rolls]. Integer 0-5 showing value on dice.

states: numpy array, dim [1, n_rolls]. Integer 0, 1. 0 if dice is fair.

"""

loglikelihoods = self._data_to_likelihood(rolls)

states = torch.LongTensor(states)

sequence_loglik = self._compute_likelihood_numerator(loglikelihoods, states)

denominator = self._compute_likelihood_denominator(loglikelihoods)

return denominator - sequence_loglik

這個(gè)方法做了三件主要的事情:1)將骰子的值映射到一個(gè)似然函數(shù)上 2)計(jì)算對數(shù)似然項(xiàng)的分子 3)計(jì)算對數(shù)似然項(xiàng)的分母。

讓我們首先來處理「_data_to_likelihood」方法,它幫助我們?nèi)?zhí)行上面提到的步驟 1。我們將要做的是,創(chuàng)建一個(gè)維度為 6*2 的矩陣,其中第一列是用公平骰子擲到 1-6 的似然度,第二列是用有偏骰子擲到 1-6 的似然度。這個(gè)問題中的矩陣的形式如下所示:array([[-1.79175947, -3.21887582],

[-1.79175947, -3.21887582],

[-1.79175947, -3.21887582],

[-1.79175947, -3.21887582],

[-1.79175947, -3.21887582],

[-1.79175947, -0.22314355]])

現(xiàn)在,如果我們看到一個(gè)投擲的點(diǎn)數(shù)為「4」,我們可以直接選擇矩陣中的第四行。這個(gè)向量中的第一個(gè)元素是用公平骰子得到「4」的對數(shù)似然 log(1/6),而第二個(gè)元素是用有偏骰子得到「4」的對數(shù)似然 log(0.04)。代碼如下:def _data_to_likelihood(self, rolls):

"""Converts a numpy array of rolls (integers) to log-likelihood.

self.loglikeligood is a matrix of 6 x 2 in our case.

Input is one [1, n_rolls]

"""

log_likelihoods = self.loglikelihood[rolls]

return Variable(torch.FloatTensor(log_likelihoods), requires_grad=False)

接下來,我們將編寫計(jì)算對數(shù)似然分子和分母的方法。def _compute_likelihood_numerator(self, loglikelihoods, states):

"""Computes numerator of likelihood function for a given sequence.

We'll iterate over the sequence of states and compute the sum

of the relevant transition cost with the log likelihood of the observed

roll.

Input:

loglikelihoods: torch Variable. Matrix of n_obs x n_states.

i,j entry is loglikelihood of observing roll i given state j

states: sequence of labels

Output:

score: torch Variable. Score of assignment.

"""

prev_state = self.n_states

score = Variable(torch.Tensor([0]))

for index, state in enumerate(states):

score += self.transition[state, prev_state] + loglikelihoods[index, state]

prev_state = state

return score

def _compute_likelihood_denominator(self, loglikelihoods):

"""Implements the forward pass of the forward-backward algorithm.

We loop over all possible states efficiently using the recursive

relationship: alpha_t(j) = \sum_i alpha_{t-1}(i) * L(x_t | y_t) * C(y_t | y{t-1} = i)

Input:

loglikelihoods: torch Variable. Same input as _compute_likelihood_numerator.

This algorithm efficiently loops over all possible state sequences

so no other imput is needed.

Output:

torch Variable.

"""

# Stores the current value of alpha at timestep t

prev_alpha = self.transition[:, self.n_states] + loglikelihoods[0].view(1, -1)

for roll in loglikelihoods[1:]:

alpha_t = []

# Loop over all possible states

for next_state in range(self.n_states):

# Compute all possible costs of transitioning to next_state

feature_function = self.transition[next_state,:self.n_states].view(1, -1) +\

roll[next_state].view(1, -1).expand(1, self.n_states)

alpha_t_next_state = prev_alpha + feature_function

alpha_t.append(self.log_sum_exp(alpha_t_next_state))

prev_alpha = torch.cat(alpha_t).view(1, -1)

return self.log_sum_exp(prev_alpha)

就是這樣!我們現(xiàn)在已經(jīng)擁有了學(xué)習(xí)轉(zhuǎn)移矩陣所需的全部代碼。但是,如果我們想要在訓(xùn)練完模型之后作出預(yù)測,我們就必須編寫維特比算法:def _viterbi_algorithm(self, loglikelihoods):

"""Implements Viterbi algorithm for finding most likely sequence of labels.

Very similar to _compute_likelihood_denominator but now we take the maximum

over the previous states as opposed to the sum.

Input:

loglikelihoods: torch Variable. Same input as _compute_likelihood_denominator.

Output:

tuple. First entry is the most likely sequence of labels. Second is

the loglikelihood of this sequence.

"""

argmaxes = []

# prev_delta will store the current score of the sequence for each state

prev_delta = self.transition[:, self.n_states].view(1, -1) +\

loglikelihoods[0].view(1, -1)

for roll in loglikelihoods[1:]:

local_argmaxes = []

next_delta = []

for next_state in range(self.n_states):

feature_function = self.transition[next_state,:self.n_states].view(1, -1) +\

roll.view(1, -1) +\

prev_delta

most_likely_state = self.argmax(feature_function)

score = feature_function[0][most_likely_state]

next_delta.append(score)

local_argmaxes.append(most_likely_state)

prev_delta = torch.cat(next_delta).view(1, -1)

argmaxes.append(local_argmaxes)

final_state = self.argmax(prev_delta)

final_score = prev_delta[0][final_state]

path_list = [final_state]

# Backtrack through the argmaxes to find most likely state

for states in reversed(argmaxes):

final_state = states[final_state]

path_list.append(final_state)

return np.array(path_list), final_score

我們實(shí)現(xiàn)的代碼還有很多,但我在這里僅僅只包含了我們在理論部分討論過的核心功能。

利用數(shù)據(jù)進(jìn)行模型評價(jià)

我使用下面概率模擬得到的數(shù)據(jù),并對模型進(jìn)行評價(jià):

1.P(序列中的第一個(gè)骰子為公平骰子)=0.5

2.P(當(dāng)前為公平骰子|上一次為公平骰子)=0.8

3.P(當(dāng)前為有偏骰子|上一次為有偏骰子)=0.35

請查看我編寫的 Notebook 去看看我是如何生成條件隨機(jī)場模型并且訓(xùn)練它的。

我們要做的第一件事就是看看估計(jì)出的轉(zhuǎn)移矩陣是怎樣的。模型會學(xué)習(xí)如果在前一次使用了公平骰子,那么這一次更有可能使用公平骰子進(jìn)行投擲(-1.38 < -0.87)。模型還學(xué)到在使用了有偏骰子后,我們更有可能使用公平骰子,但這和使用有偏投擲的可能性差別并不是很大(-1.38 < -0.87)。該模型在第一次投擲時(shí)給兩種骰子分配相同的代價(jià)(0.51 ~ -0.54)。array([[-0.86563134, -0.40748784, -0.54984874],

[-1.3820231 , -0.59524935, -0.516026 ]], dtype=float32)

接下來,我們來看看對于一個(gè)特定的投擲序列的預(yù)測結(jié)果如何:# observed dice rolls

array([2, 3, 4, 5, 5, 5, 1, 5, 3, 2, 5, 5, 5, 3, 5])

# corresponding labels. 0 means fair

array([0, 0, 1, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1])

# predictions

array([0, 1, 0, 1, 1, 1, 0, 0, 1, 0, 1, 1, 1, 0, 0])

模型識別出「6」的長序列(在上面的代碼中實(shí)際顯示為「5」,因?yàn)槲覀兪菑摹?」開始的)來自于有偏的骰子,這是有意義的。請注意,模型并沒有將所有的「6」都分配給有偏的骰子,就像對第八次投擲的預(yù)測那樣。這是因?yàn)樵谶@個(gè)「6」之前,我們很確信使用了公平骰子(我們擲出了一個(gè)「2」)并且從公平骰子狀態(tài)轉(zhuǎn)換到有偏骰子狀態(tài)的可能性較小。我認(rèn)為這種錯(cuò)誤是可以接受的,因此模型是很成功的。

結(jié)論

我向你展示了條件隨機(jī)場背后的一小部分理論知識,同時(shí)也展示了你如何才能實(shí)現(xiàn)一個(gè)用于簡單問題的條件隨機(jī)場。當(dāng)然,相關(guān)的知識遠(yuǎn)遠(yuǎn)比我在這里所能夠涵蓋到的要多。所以我建議各位讀者查看更多相關(guān)的資源。

創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎勵(lì)來咯,堅(jiān)持創(chuàng)作打卡瓜分現(xiàn)金大獎

總結(jié)

以上是生活随笔為你收集整理的条件随机场 python_如何直观地理解条件随机场,并通过PyTorch简单地实现的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。