|
| 1 | +# coding:UTF-8 |
| 2 | +''' |
| 3 | +Date:20160831 |
| 4 | +@author: zhaozhiyong |
| 5 | +''' |
| 6 | +import numpy as np |
| 7 | +from random import normalvariate # 正态分布 |
| 8 | + |
| 9 | +def loadDataSet(data): |
| 10 | + '''导入训练数据 |
| 11 | + input: data(string)训练数据 |
| 12 | + output: dataMat(list)特征 |
| 13 | + labelMat(list)标签 |
| 14 | + ''' |
| 15 | + dataMat = [] |
| 16 | + labelMat = [] |
| 17 | + fr = open(data) # 打开文件 |
| 18 | + for line in fr.readlines(): |
| 19 | + lines = line.strip().split("\t") |
| 20 | + lineArr = [] |
| 21 | + |
| 22 | + for i in xrange(len(lines) - 1): |
| 23 | + lineArr.append(float(lines[i])) |
| 24 | + dataMat.append(lineArr) |
| 25 | + |
| 26 | + #labelMat.append(float(lines[-1]) * 2 - 1) # 转换成{-1,1} |
| 27 | + labelMat.append(float(lines[-1])) |
| 28 | + fr.close() |
| 29 | + return dataMat, labelMat |
| 30 | + |
| 31 | +def sigmoid(inx): |
| 32 | + return 1.0 / (1 + np.exp(-inx)) |
| 33 | + |
| 34 | +def initialize_v(n, k): |
| 35 | + '''初始化交叉项 |
| 36 | + input: n(int)特征的个数 |
| 37 | + k(int)FM模型的度 |
| 38 | + output: v(mat):交叉项的系数权重 |
| 39 | + ''' |
| 40 | + v = np.mat(np.zeros((n, k))) |
| 41 | + |
| 42 | + for i in xrange(n): |
| 43 | + for j in xrange(k): |
| 44 | + # 利用正态分布生成每一个权重 |
| 45 | + v[i, j] = normalvariate(0, 0.2) |
| 46 | + return v |
| 47 | + |
| 48 | +def stocGradAscent(dataMatrix, classLabels, k, max_iter, alpha): |
| 49 | + '''利用随机梯度下降法训练FM模型 |
| 50 | + input: dataMatrix(mat)特征 |
| 51 | + classLabels(mat)标签 |
| 52 | + k(int)v的维数 |
| 53 | + max_iter(int)最大迭代次数 |
| 54 | + alpha(float)学习率 |
| 55 | + output: w0(float),w(mat),v(mat):权重 |
| 56 | + ''' |
| 57 | + m, n = np.shape(dataMatrix) |
| 58 | + # 1、初始化参数 |
| 59 | + w = np.zeros((n, 1)) # 其中n是特征的个数 |
| 60 | + w0 = 0 # 偏置项 |
| 61 | + v = initialize_v(n, k) # 初始化V |
| 62 | + |
| 63 | + # 2、训练 |
| 64 | + for it in xrange(max_iter): |
| 65 | + for x in xrange(m): # 随机优化,对每一个样本而言的 |
| 66 | + inter_1 = dataMatrix[x] * v |
| 67 | + inter_2 = np.multiply(dataMatrix[x], dataMatrix[x]) * \ |
| 68 | + np.multiply(v, v) # multiply对应元素相乘 |
| 69 | + # 完成交叉项 |
| 70 | + interaction = np.sum(np.multiply(inter_1, inter_1) - inter_2) / 2. |
| 71 | + p = w0 + dataMatrix[x] * w + interaction # 计算预测的输出 |
| 72 | + loss = sigmoid(classLabels[x] * p[0, 0]) - 1 |
| 73 | + |
| 74 | + w0 = w0 - alpha * loss * classLabels[x] |
| 75 | + for i in xrange(n): |
| 76 | + if dataMatrix[x, i] != 0: |
| 77 | + w[i, 0] = w[i, 0] - alpha * loss * classLabels[x] * dataMatrix[x, i] |
| 78 | + |
| 79 | + for j in xrange(k): |
| 80 | + v[i, j] = v[i, j] - alpha * loss * classLabels[x] * \ |
| 81 | + (dataMatrix[x, i] * inter_1[0, j] -\ |
| 82 | + v[i, j] * dataMatrix[x, i] * dataMatrix[x, i]) |
| 83 | + |
| 84 | + # 计算损失函数的值 |
| 85 | + if it % 1000 == 0: |
| 86 | + print "\t------- iter: ", it, " , cost: ", \ |
| 87 | + getCost(getPrediction(np.mat(dataTrain), w0, w, v), classLabels) |
| 88 | + |
| 89 | + # 3、返回最终的FM模型的参数 |
| 90 | + return w0, w, v |
| 91 | + |
| 92 | +def getCost(predict, classLabels): |
| 93 | + '''计算预测准确性 |
| 94 | + input: predict(list)预测值 |
| 95 | + classLabels(list)标签 |
| 96 | + output: error(float)计算损失函数的值 |
| 97 | + ''' |
| 98 | + m = len(predict) |
| 99 | + error = 0.0 |
| 100 | + for i in xrange(m): |
| 101 | + error -= np.log(sigmoid(predict[i] * classLabels[i] )) |
| 102 | + return error |
| 103 | + |
| 104 | +def getPrediction(dataMatrix, w0, w, v): |
| 105 | + '''得到预测值 |
| 106 | + input: dataMatrix(mat)特征 |
| 107 | + w(int)常数项权重 |
| 108 | + w0(int)一次项权重 |
| 109 | + v(float)交叉项权重 |
| 110 | + output: result(list)预测的结果 |
| 111 | + ''' |
| 112 | + m = np.shape(dataMatrix)[0] |
| 113 | + result = [] |
| 114 | + for x in xrange(m): |
| 115 | + |
| 116 | + inter_1 = dataMatrix[x] * v |
| 117 | + inter_2 = np.multiply(dataMatrix[x], dataMatrix[x]) * \ |
| 118 | + np.multiply(v, v) # multiply对应元素相乘 |
| 119 | + # 完成交叉项 |
| 120 | + interaction = np.sum(np.multiply(inter_1, inter_1) - inter_2) / 2. |
| 121 | + p = w0 + dataMatrix[x] * w + interaction # 计算预测的输出 |
| 122 | + pre = sigmoid(p[0, 0]) |
| 123 | + result.append(pre) |
| 124 | + return result |
| 125 | + |
| 126 | +def getAccuracy(predict, classLabels): |
| 127 | + '''计算预测准确性 |
| 128 | + input: predict(list)预测值 |
| 129 | + classLabels(list)标签 |
| 130 | + output: float(error) / allItem(float)错误率 |
| 131 | + ''' |
| 132 | + m = len(predict) |
| 133 | + allItem = 0 |
| 134 | + error = 0 |
| 135 | + for i in xrange(m): |
| 136 | + allItem += 1 |
| 137 | + if float(predict[i]) < 0.5 and classLabels[i] == 1.0: |
| 138 | + error += 1 |
| 139 | + elif float(predict[i]) >= 0.5 and classLabels[i] == -1.0: |
| 140 | + error += 1 |
| 141 | + else: |
| 142 | + continue |
| 143 | + return float(error) / allItem |
| 144 | + |
| 145 | +def save_model(file_name, w0, w, v): |
| 146 | + '''保存训练好的FM模型 |
| 147 | + input: file_name(string):保存的文件名 |
| 148 | + w0(float):偏置项 |
| 149 | + w(mat):一次项的权重 |
| 150 | + v(mat):交叉项的权重 |
| 151 | + ''' |
| 152 | + f = open(file_name, "w") |
| 153 | + # 1、保存w0 |
| 154 | + f.write(str(w0) + "\n") |
| 155 | + # 2、保存一次项的权重 |
| 156 | + w_array = [] |
| 157 | + m = np.shape(w)[0] |
| 158 | + for i in xrange(m): |
| 159 | + w_array.append(str(w[i, 0])) |
| 160 | + f.write("\t".join(w_array) + "\n") |
| 161 | + # 3、保存交叉项的权重 |
| 162 | + m1 , n1 = np.shape(v) |
| 163 | + for i in xrange(m1): |
| 164 | + v_tmp = [] |
| 165 | + for j in xrange(n1): |
| 166 | + v_tmp.append(str(v[i, j])) |
| 167 | + f.write("\t".join(v_tmp) + "\n") |
| 168 | + f.close() |
| 169 | + |
| 170 | + |
| 171 | +if __name__ == "__main__": |
| 172 | + # 1、导入训练数据 |
| 173 | + print "---------- 1.load data ---------" |
| 174 | + dataTrain, labelTrain = loadDataSet("data_1.txt") |
| 175 | + print "---------- 2.learning ---------" |
| 176 | + # 2、利用随机梯度训练FM模型 |
| 177 | + w0, w, v = stocGradAscent(np.mat(dataTrain), labelTrain, 2, 20000, 0.01) |
| 178 | + predict_result = getPrediction(np.mat(dataTrain), w0, w, v) # 得到训练的准确性 |
| 179 | + print "----------training accuracy: %f" % (1 - getAccuracy(predict_result, labelTrain)) |
| 180 | + print "---------- 3.save result ---------" |
| 181 | + # 3、保存训练好的FM模型 |
| 182 | + save_model("weights", w0, w, v) |
0 commit comments