當前位置: 首頁>>機器學習>>正文


揭開機器學習的麵紗:最大熵模型100行代碼實現[Python版]

理論說明部分見上一篇:

最大熵模型簡介[例子+推導+GIS求解]

為了是代碼簡短,方便閱讀,去掉了很多健壯性檢測的代碼以及特殊處理。下麵的代碼實現的是:使用最基礎GIS訓練最大熵模型。GIS由於性能問題在實際中不適用,但是可以幫助我們理解最大熵訓練到底在做什麽。


#!/usr/bin/python
#coding=utf8
import sys;
import math;
from collections import defaultdict

class MaxEnt:
	def __init__(self):
		self._samples = []; 	#樣本集, 元素是[y,x1,x2,...,xn]的元組
		self._Y = set([]); #標簽集合,相當於去重之後的y
		self._numXY = defaultdict(int);  #Key是(xi,yi)對,Value是count(xi,yi)
		self._N = 0;		#樣本數量
		self._n = 0;		#特征對(xi,yi)總數量
		self._xyID = {};	#對(x,y)對做的順序編號(ID), Key是(xi,yi)對,Value是ID
		self._C = 0;		#樣本最大的特征數量,用於求參數時的迭代,見IIS原理說明
		self._ep_ = [];		#樣本分布的特征期望值
		self._ep = [];		#模型分布的特征期望值
		self._w = [];		#對應n個特征的權值
		self._lastw = [];	#上一輪迭代的權值
		self._EPS = 0.01;	#判斷是否收斂的閾值
	def load_data(self, filename):
		for line in open(filename, "r"):
			sample = line.strip().split("\t");
			if len(sample) < 2: #至少:標簽+一個特征
				continue;
			y = sample[0];
			X = sample[1:];
			self._samples.append(sample); #labe + features
			self._Y.add(y); #label
			for x in set(X): #set給X去重
				self._numXY[(x, y)] += 1;
	def _initparams(self):
		self._N = len(self._samples); 
		self._n = len(self._numXY);
		self._C = max([len(sample) - 1 for sample in self._samples]);
		self._w = [0.0] * self._n;
		self._lastw = self._w[:];
		self._sample_ep();
	def _convergence(self):
		for w, lw in zip(self._w, self._lastw):
			if math.fabs(w - lw) >= self._EPS:
				return False;
		return True;
	def _sample_ep(self):
		self._ep_ = [0.0] * self._n;
		#計算方法參見公式(20)
		for i, xy in enumerate(self._numXY):
			self._ep_[i] = self._numXY[xy] * 1.0 / self._N;
			self._xyID[xy] = i;
	def _zx(self, X):
		#calculate Z(X), 計算方法參見公式(15)
		ZX = 0.0;
		for y in self._Y:
			sum = 0.0;
			for x in X:
				if (x, y) in self._numXY:
					sum += self._w[self._xyID[(x, y)]];
			ZX += math.exp(sum);
		return ZX;
	def _pyx(self, X):
		#calculate p(y|x), 計算方法參見公式(22)
		ZX = self._zx(X);
		results = [];
		for y in self._Y:
			sum = 0.0;
			for x in X:
				if (x, y) in self._numXY: #這個判斷相當於指示函數的作用
					sum += 	self._w[self._xyID[(x, y)]];
			pyx = 1.0 / ZX * math.exp(sum);
			results.append((y, pyx));
		return results;
	def _model_ep(self):
		self._ep = [0.0] * self._n;
		#參見公式(21)
		for sample in self._samples:
			X = sample[1:];
			pyx = self._pyx(X);
			for y, p in pyx:
				for x in X:
					if (x, y) in self._numXY:
						self._ep[self._xyID[(x, y)]] += p * 1.0 / self._N;
	def train(self, maxiter = 1000):
		self._initparams();
		for i in range(0, maxiter):
			print "Iter:%d..."%i;
			self._lastw = self._w[:]; #保存上一輪權值
			self._model_ep();
	 		#更新每個特征的權值
			for i, w in enumerate(self._w):
				#參考公式(19)
				self._w[i] += 1.0 / self._C * math.log(self._ep_[i] / self._ep[i]);
			print self._w;
			#檢查是否收斂	
			if self._convergence():
				break;
	def predict(self, input):
		X = input.strip().split("\t");
		prob = self._pyx(X)
		return prob;

if __name__ == "__main__":
	maxent = MaxEnt();
	maxent.load_data('data.txt');
	maxent.train();
	print maxent.predict("sunny\thot\thigh\tFALSE"); 
	print maxent.predict("overcast\thot\thigh\tFALSE");
	print maxent.predict("sunny\tcool\thigh\tTRUE");
	sys.exit(0);

訓練數據來自各種天氣情況下是否打球的例子:data.txt
其中字段依次是:

play outlook temperature humidity windy

 
部分運行結果:

xyz

本文由《純淨天空》出品。文章地址: https://vimsky.com/zh-tw/article/776.html,未經允許,請勿轉載。