├── 1.perceptron ├── README.md ├── model.py └── train.py ├── 2.KNN ├── README.md └── model.py ├── 3.naive_bayes ├── README.md └── model.py ├── 4.DecisionTree ├── README.md ├── model.py └── treePlotter.py ├── 5. Adaboost ├── README.md ├── adaboost.py ├── logicalRegression.py ├── result.png ├── test.txt ├── train.txt └── utils.py ├── Optimization_method ├── 1.Gradient_decent.ipynb ├── 2. 牛顿法.ipynb └── 3.拟牛顿法.ipynb ├── README.md └── RNN ├── LSTM.ipynb └── RNN.ipynb /1.perceptron/README.md: -------------------------------------------------------------------------------- 1 | # 感知机 2 | 3 | ## 使用方式 4 | 5 | X = np.asarray([[1, 1], [3, 3], [4, 3]]) 6 | y = np.asarray([[-1], [1], [1]]) 7 | model = Perceptron() 8 | model.fit( X, y) 9 | 10 | ## 模型定义 11 | 12 | y = sign(w * x + b) 13 | 14 | ## 损失函数 15 | 利用分错的点到感知机的距离来当作损失函数 16 | 17 | ## 优化方式 18 | 19 | w += 学习率 * y * x 20 | b += 学习率 * y 21 | 22 | 以此方式来来回迭代,完成w和b的修改 -------------------------------------------------------------------------------- /1.perceptron/model.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | # @Time : 2018/3/17 下午8:07 4 | # @Author : zhanzecheng 5 | # @File : model.py 6 | # @Software: PyCharm 7 | """ 8 | import numpy as np 9 | import random 10 | 11 | class Perceptron: 12 | ''' 13 | 感知机的定义为 f(x) = sign(w * x + b) 14 | 其中 w是n维向量(n为训练数据的维度) 15 | b是一维 16 | ''' 17 | def __init__(self): 18 | 19 | self._W = [] 20 | self._b = 0 21 | self._epochs = 100 22 | self._learningRate = 1 23 | 24 | 25 | def _forword(self, X): 26 | ''' 27 | 感知机的前向传播 28 | :param X: 训练数据 29 | :return: 感知机的分类结果 30 | ''' 31 | logit = np.zeros((len(X), 1)) 32 | 33 | for count, x in enumerate(X): 34 | tmp = 1 if np.matmul(self._W , x) + self._b > 0 else -1 35 | logit[count] = tmp 36 | return logit 37 | 38 | def _loss(self, logit, label): 39 | ''' 40 | 感知机分错的个数和索引 41 | :param logit: 感知机预测的结果 42 | :param label: 训练集真实的结果 43 | :return: 感知机分错的个数和索引 44 | ''' 45 | count = 0 46 | wrong = [] 47 | for index, (a, b )in enumerate(zip(logit, label)): 48 | if a != b: 49 | count += 1 50 | wrong.append(index) 51 | 52 | self._wrong = count 53 | return wrong 54 | 55 | def _backford(self, X_wrong, y): 56 | ''' 57 | 更新感知机的参数 58 | :param X_wrong: 随机选取的分错的点 59 | :param y: 随机选取的分错的真实坐标 60 | :return: 61 | ''' 62 | self._W = self._W + y * X_wrong * self._learningRate 63 | self._b = self._b + y * self._learningRate 64 | 65 | def fit(self, X_train, y_train): 66 | ''' 67 | 训练感知机 68 | :param X_train: 训练集 69 | :param y_train: 标签 70 | :return: 71 | ''' 72 | self._W = np.zeros(shape=(1, X_train.shape[1])) 73 | for epoch in range(self._epochs): 74 | # 1. First we should do the forward 75 | logit = self._forword(X_train) 76 | 77 | # 2. Then we should use stochastic gradient descent to optimize our W & b 78 | wrong = self._loss(logit=logit, label=y_train) 79 | 80 | if len(wrong) == 0: 81 | print('we succeed the training') 82 | break 83 | 84 | X_wrong_index = random.choice(wrong) 85 | self._backford(X_train[X_wrong_index], y_train[X_wrong_index]) 86 | 87 | print('Now we have ', str(self._wrong), ' wrong points') 88 | print('Now we finish our training') 89 | 90 | def predict(self, X_test): 91 | ''' 92 | 利用感知机来预测 93 | :param X_test: 测试集 94 | :return: 95 | ''' 96 | return self._forword(X_test) 97 | 98 | def test(): 99 | X = np.asarray([[1, 1], [3, 3], [4, 3]]) 100 | y = np.asarray([[-1], [1], [1]]) 101 | model = Perceptron() 102 | model.fit( X, y) 103 | 104 | -------------------------------------------------------------------------------- /1.perceptron/train.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | # @Time : 2018/3/17 下午9:09 4 | # @Author : zhanzecheng 5 | # @File : train.py 6 | # @Software: PyCharm 7 | """ 8 | 9 | import numpy as np 10 | 11 | from model import Perceptron 12 | 13 | def main(): 14 | X = np.asarray([[1, 1], [3, 3], [4, 3]]) 15 | y = np.asarray([[-1], [1], [1]]) 16 | model = Perceptron() 17 | model.fit( X, y) 18 | 19 | if __name__ == '__main__': 20 | main() 21 | -------------------------------------------------------------------------------- /2.KNN/README.md: -------------------------------------------------------------------------------- 1 | # KD树实现的K邻近算法 2 | 3 | ## 使用方式 4 | 5 | data = [[1, 2], [2, 8], [3, 4], [4, 7], [2, 6], [6, 22], [7, 8]] 6 | kdtree = KDTree(data) 7 | distance, point = kdtree.findNN([1,1], 3) 8 | 9 | ## 模型定义 10 | 11 | 利用kd树来划分特征空间 12 | 13 | ## 损失函数 14 | 不含显性的学习过程 15 | 16 | -------------------------------------------------------------------------------- /2.KNN/model.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | # @Time : 2018/5/21 上午9:50 4 | # @Author : zhanzecheng 5 | # @File : model.py 6 | # @Software: PyCharm 7 | """ 8 | import numpy as np 9 | 10 | class KD_node(): 11 | #定义的kd树节点 12 | def __init__(self, point=None, split=None, LL=None, RR=None): 13 | #节点值 14 | self.point = point 15 | #节点分割维度 16 | self.split = split 17 | #节点左孩子 18 | self.left = LL 19 | #节点右孩子 20 | self.right = RR 21 | 22 | 23 | class KDTree: 24 | def __init__(self, data=None): 25 | ''' 26 | 建树 27 | :param data: 28 | ''' 29 | self.root = None 30 | self.root = self._createNode(self.root, split=self._maxVar(data), data=data) 31 | 32 | pass 33 | 34 | def _createNode(self, root, split=None, data=None): 35 | ''' 36 | 创建kd树 37 | :param split: 38 | :param data: 39 | :return: 40 | ''' 41 | if len(data) == 0: 42 | return None 43 | # 在以split划分的维度上找到中位数 44 | data = list(data) 45 | data.sort(key= lambda x : x[split]) 46 | data = np.array(data) 47 | # 下面用来求中位数 48 | median = len(data) // 2 49 | # 下面递归建立左右子树 50 | root = KD_node(data[median], split) 51 | 52 | root.left = self._createNode(root.left, 53 | split=self._maxVar(data[:median]), 54 | data=data[:median]) 55 | root.right = self._createNode(root.left, 56 | split=self._maxVar(data[median+1:]), 57 | data=data[median+1:]) 58 | return root 59 | 60 | 61 | def _maxVar(self, data=None): 62 | ''' 63 | 用来求数据方差最大的维度 64 | :param data: 65 | :return: 66 | ''' 67 | if len(data) == 0: 68 | return 0 69 | # 按列求均值 70 | data_mean = np.mean(data, axis=0) 71 | # numpy 按列减是直接减的 72 | mean_diff = data - data_mean 73 | # 求得方差 74 | data_var = np.sum(mean_diff ** 2, axis=0) / len(data) 75 | # 求得方差最大位置, 为所要划分的维度 76 | re = np.where(data_var == np.max(data_var)) 77 | return re[0][0] 78 | 79 | 80 | # 下面是kdtree的查找 81 | def _computeDist(self, pt1, pt2): 82 | ''' 83 | 计算两个实例点的特征距离 84 | :param pt1: first 85 | :param pt2: second 86 | :return: float 87 | ''' 88 | pt1 = np.array(pt1) 89 | pt2 = np.array(pt2) 90 | return np.sqrt(np.sum(np.square((pt1 - pt2)))) 91 | 92 | def findNN(self, query, k): 93 | ''' 94 | 查看目标点的最近k个点 95 | :param query: 需要查询的点 96 | :param k: 需要多少个临近点 97 | :return: 98 | ''' 99 | node_K = [] 100 | nodeList = [] 101 | result = [] 102 | temp_root = self.root 103 | # 为了方便,在找到叶子节点同时,把所走过的父节点的距离都保存下来,下一次回溯访问就只需要访问子节点,不需要再访问一遍父节点。 104 | # 下面是为了找到目标点在KD树的划分 105 | while temp_root: 106 | nodeList.append(temp_root) 107 | dd = self._computeDist(query, temp_root.point) 108 | if len(node_K) < k: 109 | node_K.append(dd) 110 | result.append(temp_root.point) 111 | else: 112 | # 选出队列里面最大的元素 113 | max_dist = max(node_K) 114 | if dd < max_dist: 115 | # 类似于优先队列 把该元素pop出来 116 | # TODO: 换成优先队列来实现 117 | index = node_K.index(max_dist) 118 | del node_K[index], result[index] 119 | node_K.append(dd) 120 | result.append(temp_root.point) 121 | ss = temp_root.split 122 | # 找到最靠近的叶子节点 123 | if query[ss] <= temp_root.point[ss]: 124 | temp_root = temp_root.left 125 | else: 126 | temp_root = temp_root.right 127 | 128 | # 回溯访问父节点 129 | while nodeList: 130 | back_point = nodeList.pop() 131 | ss = back_point.split 132 | print('父亲节点 : ', back_point.point, '维度 :', back_point.split) 133 | max_dist = max(node_K) 134 | # 若满足进入该父节点的另外一个子节点的条件 135 | if len(node_K) < k or abs(query[ss] - back_point.point[ss]) < max_dist: 136 | # 进入另外一个子节点 137 | if query[ss] <= back_point.point[ss]: 138 | temp_root = back_point.right 139 | else: 140 | temp_root = back_point.left 141 | # 若不是叶子节点 142 | if temp_root: 143 | nodeList.append(temp_root) 144 | curDist = self._computeDist(temp_root.point, query) 145 | 146 | if max_dist > curDist and len(node_K) == k: 147 | index = node_K.index(max_dist) 148 | del node_K[index], result[index] 149 | node_K.append(curDist) 150 | result.append(temp_root.point) 151 | elif len(node_K) < k: 152 | node_K.append(curDist) 153 | result.append(temp_root.point) 154 | 155 | return node_K, result 156 | 157 | 158 | 159 | 160 | if __name__ == '__main__': 161 | data = [[1, 2], [2, 8], [3, 4], [4, 7], [2, 6], [6, 22], [7, 8]] 162 | 163 | kdtree = KDTree(data) 164 | distance, point = kdtree.findNN([1,1], 3) 165 | print('----> distance: ', distance) 166 | print('----> point: ', point) 167 | 168 | 169 | 170 | 171 | 172 | 173 | -------------------------------------------------------------------------------- /3.naive_bayes/README.md: -------------------------------------------------------------------------------- 1 | # 朴素贝叶斯实现 2 | 3 | ## 使用方式 4 | 5 | cls = NBayes() 6 | x = [[1, 2], [2, 3], [2, 1]] 7 | y = [0, 1, 1] 8 | cls.train(x, y) 9 | print(cls.predict([2, 1])) 10 | 11 | ## 模型定义 12 | 13 | 利用贝叶斯定理于特征条件独立假设 14 | 15 | ## 损失函数 16 | 0-1损失函数 17 | 18 | -------------------------------------------------------------------------------- /3.naive_bayes/model.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | # @Time : 2018/5/22 上午10:00 4 | # @Author : zhanzecheng 5 | # @File : model.py 6 | # @Software: PyCharm 7 | """ 8 | import numpy as np 9 | 10 | class NBayes: 11 | def __init__(self): 12 | pass 13 | 14 | def _getProbality(self, trainData, trainLabel): 15 | """ 16 | 目前实现的是二分类的朴素贝叶斯 17 | :param trainData: 18 | :param trainLabel: 19 | :return: 20 | """ 21 | if type(trainData) != np.ndarray: 22 | print('---> convert train type to array') 23 | dataLen = len(trainData) 24 | # 得到总共有多少特征 25 | # 计算类别1发生的概率 26 | pAbusive = sum(trainLabel) / dataLen 27 | p0 = [] 28 | p1 = [] 29 | 30 | featureNum = len(trainData[0]) 31 | for feature in range(featureNum): 32 | numsWord = len(trainData[0][feature]) 33 | # 1初始化,防止概率为0的情况. 这里使用的是拉普拉斯平滑方式 34 | p0Num = np.ones(numsWord) 35 | p1Num = np.ones(numsWord) 36 | # 以拉普拉斯平滑方式来初始化分母 37 | p0Denom = 2.0 38 | p1Denom = 2.0 39 | for i in range(dataLen): 40 | if trainLabel[i] == 1: 41 | # 利用了numpy的矩阵相加便捷性 42 | p1Num += trainData[i][feature] 43 | p1Denom += sum(trainData[i][feature]) 44 | else: 45 | p0Num += trainData[i][feature] 46 | p0Denom += sum(trainData[i][feature]) 47 | # 这里利用log的性质进行变换,可以把原来相乘的表达式变成相加 48 | p1Vect = np.log(p1Num / p1Denom) 49 | p0Vect = np.log(p0Num / p0Denom) 50 | p0.append(p0Vect) 51 | p1.append(p1Vect) 52 | return p0, p1, pAbusive 53 | 54 | 55 | def _to_categorical(self, y, num_classes=None): 56 | """ 57 | Converts a class vector (integers) to binary class matrix. 58 | """ 59 | y = np.array(y, dtype='int') 60 | input_shape = y.shape 61 | if input_shape and input_shape[-1] == 1 and len(input_shape) > 1: 62 | input_shape = tuple(input_shape[:-1]) 63 | y = y.ravel() 64 | if not num_classes: 65 | num_classes = np.max(y) + 1 66 | n = y.shape[0] 67 | categorical = np.zeros((n, num_classes), dtype=np.float32) 68 | categorical[np.arange(n), y] = 1 69 | output_shape = input_shape + (num_classes,) 70 | categorical = np.reshape(categorical, output_shape) 71 | return categorical 72 | 73 | def train(self, x, y=None): 74 | x = self._to_categorical(x) 75 | self.x = x 76 | self.p0Vec, self.p1Vec, self.pA = self._getProbality(x, y) 77 | 78 | def predict(self, test): 79 | """ 80 | 这里仅实现了对于batch_size为1的predict 81 | :param test: 82 | :return: 83 | """ 84 | x = self.x[0] 85 | m, n = x.shape 86 | predict = np.zeros((m, n)) 87 | for row, d in enumerate(test): 88 | # TODO: 这里对于没有见过的特征值,仅是简单的赋值为固定值 89 | if d > n: 90 | d = 0 91 | predict[row][int(d)] = 1 92 | 93 | p1 = np.log(self.pA) 94 | for vec2Classify, p1Vec in zip(predict, self.p1Vec): 95 | p1 += sum(vec2Classify * p1Vec) 96 | 97 | p0 = np.log(1.0 - self.pA) 98 | for vec2Classify, p0Vec in zip(predict, self.p0Vec): 99 | p0 += sum(vec2Classify * p0Vec) 100 | 101 | if p1 > p0: 102 | return 1 103 | else: 104 | return 0 105 | 106 | if __name__ == '__main__': 107 | cls = NBayes() 108 | x = [[1, 2], [2, 3], [2, 1]] 109 | y = [0, 1, 1] 110 | cls.train(x, y) 111 | print(cls.predict([2, 1])) -------------------------------------------------------------------------------- /4.DecisionTree/README.md: -------------------------------------------------------------------------------- 1 | # C4.5方式生成的决策树 2 | 3 | ## 使用方式 4 | 5 | data = [ 6 | [1, 2, 3], 7 | [1, 2, 4], 8 | [1, 2, 5], 9 | [0, 2, 1], 10 | [0, 11, 2] 11 | ] 12 | label = [0, 0, 0, 1, 1] 13 | cls = DecisionTree() 14 | # 训练决策树 15 | cls.train(data, label) 16 | 17 | data = [1, 1, 1] 18 | # 测试决策树 19 | print(cls.predict(data)) 20 | 21 | ## 模型定义 22 | 23 | 利用C4.5算法来生成决策树 24 | 25 | ## 损失函数 26 | 不含剪枝过程 27 | 28 | ## TODO: 29 | 增加正则化: 30 | 信息增益最小限度 31 | 剪枝过程 32 | 33 | -------------------------------------------------------------------------------- /4.DecisionTree/model.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | # @Time : 2018/5/23 上午10:13 4 | # @Author : zhanzecheng 5 | # @File : model.py 6 | # @Software: PyCharm 7 | """ 8 | import math 9 | 10 | class DecisionTree: 11 | def __init__(self): 12 | pass 13 | 14 | def _calcShannonEnt(self, dataSet): 15 | """ 16 | 该函数是用来计算 label 熵 H(x) = -sum(p * log(p)) 17 | :param dataSet: 18 | :return: 19 | """ 20 | dataLen = len(dataSet) 21 | labelCount = {} 22 | for featVec in dataSet: 23 | label = featVec[-1] 24 | if label not in labelCount.keys(): 25 | labelCount[label] = 1 26 | else: 27 | labelCount[label] += 1 28 | shannonEnt = 0.0 29 | # 这里用这个循环来做公式中的sum 30 | for key in labelCount: 31 | prob = labelCount[key] / dataLen 32 | shannonEnt -= prob * math.log(prob, 2) 33 | 34 | return shannonEnt 35 | 36 | def _chooseBestFeatureToSplit(self, dataSet): 37 | """ 38 | 利用[信息增益比]来选择最佳划分维度, 信息增益比相对于信息增益可以优化有较多种类的特征 39 | :param dataSet: 40 | :return: 41 | """ 42 | # 其中- 1是要减去标签 43 | numFeatures = len(dataSet[0]) - 1 44 | baseEntropy = self._calcShannonEnt(dataSet) 45 | bestInfoGainRation = 0.0 46 | bestFeature = -1 47 | for i in range(numFeatures): 48 | # 取出该种特征对应的值 49 | featList = [example[i] for example in dataSet] 50 | uniqueVals = set(featList) 51 | newEntropy = 0 52 | splitInfo = 0 53 | for value in uniqueVals: 54 | subDataSet = self._splitDataSet(dataSet, i, value) # 每个唯一值对应的剩余feature的组成子集 55 | prob = len(subDataSet) / float(len(dataSet)) 56 | newEntropy += prob * self._calcShannonEnt(subDataSet) 57 | splitInfo += -prob * math.log(prob, 2) 58 | infoGain = baseEntropy - newEntropy 59 | if (splitInfo == 0): # fix the overflow bug 60 | continue 61 | infoGainRatio = infoGain / splitInfo #这个feature的infoGainRatio 62 | if (infoGainRatio > bestInfoGainRation): #选择最大的gain ratio 63 | bestInfoGainRation = infoGainRatio 64 | bestFeature = i #选择最大的gain ratio对应的feature 65 | return bestFeature 66 | 67 | def _majorityCnt(self, classList): 68 | """ 69 | 以投票机制来选出max类别 70 | :param classList: 71 | :return: 72 | """ 73 | classCount = {} 74 | for vote in classList: 75 | if vote not in classCount.keys(): 76 | classCount[vote] = 0 77 | classCount[vote] += 1 78 | 79 | sortedClassCount = sorted(classCount.items(), key=lambda x: x[1], reverse=True) 80 | return sortedClassCount[0][0] 81 | 82 | def _splitDataSet(self, dataSet, axis, value): 83 | """ 84 | 输入:数据集,选择维度,选择值 85 | 输出:划分数据集 86 | 描述:按照给定特征划分数据集;去除选择维度中等于选择值的项 87 | :param dataSet: 88 | :param axos: 89 | :param value: 90 | :return: 91 | """ 92 | retDataSet = [] 93 | for featVec in dataSet: 94 | if featVec[axis] == value: # 只看当第i列的值=value时的item 95 | reduceFeatVec = featVec[:].copy() 96 | del reduceFeatVec[axis] 97 | retDataSet.append(reduceFeatVec) 98 | return retDataSet 99 | 100 | def _createTree(self, dataSet, labels): 101 | """ 102 | 以递归的方式来构造决策树 103 | 伪代码: 104 | 1)若数据集中所有实例属于同一类C,则T为单节点树,并将类C作为该节点的类标记,返回T 105 | 2)若特征(A)为空,则返回D中实例中出现最多的类别作为该节点的类别,返回T 106 | 3)否则,计算A中各特征对D的信息增益,选择信息增益最大的特征Ag 107 | 4)[可选]如果Ag小于阈值e,则置T为单节点树,并将D中实例数最大的类作为该节点的标记类,返回T 108 | 5)否则,对Ag的每一可能值ai,依Ag=ai将D分割为若干非空子集Di,对其递归的进行调用 109 | :param dataSet: 110 | :param labels: 111 | :return: 112 | """ 113 | 114 | # 得到数据集中各类别 115 | classList = [example[-1] for example in dataSet] 116 | # 如果所有实例都属于同一类C, 则停止划分 117 | if classList.count(classList[0]) == len(classList): 118 | return classList[0] 119 | # 如果特征为空,则返回出现次数最多的类 120 | if len(dataSet[0]) == 1: 121 | return self._majorityCnt(classList) 122 | 123 | # 否则,计算信息熵增益,并且选取最大的作为分类标准 124 | # TODO: implement the function 125 | bestFeat = self._chooseBestFeatureToSplit(dataSet) 126 | 127 | # 获得特征名 128 | bestFeatName = labels[bestFeat] 129 | # 用字典的方式来建立树 130 | myTree = {bestFeatName:{}} 131 | 132 | # 剔除该特征 133 | del labels[bestFeat] 134 | 135 | # 得到该特征所有的可能取值 136 | featValues = [example[bestFeat] for example in dataSet] 137 | uniqueVals = set(featValues) 138 | for value in uniqueVals: 139 | subLabels = labels[:] 140 | # 递归的建造树 141 | myTree[bestFeatName][value] = self._createTree(self._splitDataSet(dataSet, bestFeat, value), subLabels) 142 | 143 | return myTree 144 | 145 | def _classify(self, inputTree, featLabels, testVec): 146 | """ 147 | 递归的找出测试数据所属于的类别 148 | :param inputTree: 149 | :param featLabels: 150 | :param testVec: 151 | :return: 152 | """ 153 | firstStr = list(inputTree.keys())[0] 154 | secondDict = inputTree[firstStr] 155 | featIndex = featLabels.index(firstStr) 156 | # 递归的访问分类树 157 | for key in secondDict.keys(): 158 | if testVec[featIndex] == key: 159 | if type(secondDict[key]).__name__ == 'dict': 160 | # 如果secondDict[key]仍然是字典,则继续向下层走 161 | classLabel = self._classify(secondDict[key], featLabels, testVec) 162 | else: 163 | # 如果secondDict[key]已经只是分类标签了,则返回这个类别标签 164 | classLabel = secondDict[key] 165 | return classLabel 166 | 167 | 168 | 169 | def train(self,dataSet, label): 170 | """ 171 | 该函数用来训练一个决策树 172 | :param dataSet: 173 | :param label: 174 | :return: 175 | """ 176 | featureNum = len(dataSet[0]) 177 | featureList = [] 178 | # 得到特征名 179 | for i in range(featureNum): 180 | featureList.append(str(i)) 181 | # 联合label和train 182 | for count, d in enumerate(dataSet): 183 | d.extend([label[count]]) 184 | 185 | self.tree = self._createTree(dataSet, featureList) 186 | 187 | def predict(self, dataSet): 188 | """ 189 | 利用训练好的决策树来进行分类 190 | :param data: 191 | :return: 192 | """ 193 | featureNum = len(dataSet) 194 | featureList = [] 195 | # 得到特征名 196 | for i in range(featureNum): 197 | featureList.append(str(i)) 198 | 199 | return self._classify(self.tree, featLabels=featureList, testVec=dataSet) 200 | 201 | 202 | 203 | 204 | if __name__ == '__main__': 205 | data = [ 206 | [1, 2, 3], 207 | [1, 2, 4], 208 | [1, 2, 5], 209 | [0, 2, 1], 210 | [0, 11, 2] 211 | ] 212 | label = [0, 0, 0, 1, 1] 213 | cls = DecisionTree() 214 | # 训练决策树 215 | cls.train(data, label) 216 | print(cls.tree) 217 | data = [1, 1, 1] 218 | # 测试决策树 219 | print(cls.predict(data)) -------------------------------------------------------------------------------- /4.DecisionTree/treePlotter.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | # @Time : 2018/5/23 上午10:27 4 | # @Author : zhanzecheng 5 | # @File : treePlotter.py 6 | # @Software: PyCharm 7 | """ 8 | import matplotlib.pyplot as plt 9 | 10 | decisionNode = dict(boxstyle="sawtooth", fc="0.8") 11 | leafNode = dict(boxstyle="round4", fc="0.8") 12 | arrow_args = dict(arrowstyle="<-") 13 | 14 | def plotNode(nodeTxt, centerPt, parentPt, nodeType): 15 | createPlot.ax1.annotate(nodeTxt, xy=parentPt, xycoords='axes fraction', \ 16 | xytext=centerPt, textcoords='axes fraction', \ 17 | va="center", ha="center", bbox=nodeType, arrowprops=arrow_args) 18 | 19 | def getNumLeafs(myTree): 20 | numLeafs = 0 21 | firstStr = list(myTree.keys())[0] 22 | secondDict = myTree[firstStr] 23 | for key in secondDict.keys(): 24 | if type(secondDict[key]).__name__ == 'dict': 25 | numLeafs += getNumLeafs(secondDict[key]) 26 | else: 27 | numLeafs += 1 28 | return numLeafs 29 | 30 | def getTreeDepth(myTree): 31 | maxDepth = 0 32 | firstStr = list(myTree.keys())[0] 33 | secondDict = myTree[firstStr] 34 | for key in secondDict.keys(): 35 | if type(secondDict[key]).__name__ == 'dict': 36 | thisDepth = getTreeDepth(secondDict[key]) + 1 37 | else: 38 | thisDepth = 1 39 | if thisDepth > maxDepth: 40 | maxDepth = thisDepth 41 | return maxDepth 42 | 43 | def plotMidText(cntrPt, parentPt, txtString): 44 | xMid = (parentPt[0] - cntrPt[0]) / 2.0 + cntrPt[0] 45 | yMid = (parentPt[1] - cntrPt[1]) / 2.0 + cntrPt[1] 46 | createPlot.ax1.text(xMid, yMid, txtString) 47 | 48 | def plotTree(myTree, parentPt, nodeTxt): 49 | numLeafs = getNumLeafs(myTree) 50 | depth = getTreeDepth(myTree) 51 | firstStr = list(myTree.keys())[0] 52 | cntrPt = (plotTree.xOff + (1.0 + float(numLeafs)) / 2.0 / plotTree.totalw, plotTree.yOff) 53 | plotMidText(cntrPt, parentPt, nodeTxt) 54 | plotNode(firstStr, cntrPt, parentPt, decisionNode) 55 | secondDict = myTree[firstStr] 56 | plotTree.yOff = plotTree.yOff - 1.0 / plotTree.totalD 57 | for key in secondDict.keys(): 58 | if type(secondDict[key]).__name__ == 'dict': 59 | plotTree(secondDict[key], cntrPt, str(key)) 60 | else: 61 | plotTree.xOff = plotTree.xOff + 1.0 / plotTree.totalw 62 | plotNode(secondDict[key], (plotTree.xOff, plotTree.yOff), cntrPt, leafNode) 63 | plotMidText((plotTree.xOff, plotTree.yOff), cntrPt, str(key)) 64 | plotTree.yOff = plotTree.yOff + 1.0 / plotTree.totalD 65 | 66 | def createPlot(inTree): 67 | fig = plt.figure(1, facecolor='white') 68 | fig.clf() 69 | axprops = dict(xticks=[], yticks=[]) 70 | createPlot.ax1 = plt.subplot(111, frameon=False, **axprops) 71 | plotTree.totalw = float(getNumLeafs(inTree)) 72 | plotTree.totalD = float(getTreeDepth(inTree)) 73 | plotTree.xOff = -0.5 / plotTree.totalw 74 | plotTree.yOff = 1.0 75 | plotTree(inTree, (0.5, 1.0), '') 76 | plt.show() -------------------------------------------------------------------------------- /5. Adaboost/README.md: -------------------------------------------------------------------------------- 1 | # Adaboost 2 | 使用 逻辑回归(Logical Regression) 作为Adaboost的基分类器 3 | 4 | ## 使用方式 5 | 6 | # define the base classify 7 | clf = LogisticRegression() 8 | 9 | # using the adaboost method 10 | er_i = adaboost_clf(y, X, y_test, X_test, i, clf) 11 | 12 | 13 | ## 任务说明 14 | 15 | 使用adaboost来做分类任务 16 | -------------------------------------------------------------------------------- /5. Adaboost/adaboost.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | import numpy as np 3 | from logicalRegression import LogisticRegression 4 | import matplotlib.pyplot as plt 5 | 6 | """ HELPER FUNCTION: GET ERROR RATE =========================================""" 7 | 8 | 9 | def get_error_rate(pred, Y): 10 | return sum(pred != Y) / float(len(Y)) 11 | 12 | 13 | """ HELPER FUNCTION: PRINT ERROR RATE =======================================""" 14 | 15 | 16 | def print_error_rate(err): 17 | print('Error rate: Training: %.4f - Test: %.4f' % err) 18 | 19 | 20 | """ HELPER FUNCTION: GENERIC CLASSIFIER =====================================""" 21 | 22 | 23 | def generic_clf(Y_train, X_train, Y_test, X_test, clf): 24 | clf.fit(X_train, Y_train) 25 | pred_train = clf.predict(X_train) 26 | pred_test = clf.predict(X_test) 27 | return get_error_rate(pred_train, Y_train), \ 28 | get_error_rate(pred_test, Y_test) 29 | 30 | 31 | """ ADABOOST IMPLEMENTATION =================================================""" 32 | 33 | def adaboost_clf(Y_train, X_train, Y_test, X_test, M, clf): 34 | n_train, n_test = len(X_train), len(X_test) 35 | # Initialize weights 36 | w = np.ones(n_train) / n_train 37 | pred_train, pred_test = [np.zeros(n_train), np.zeros(n_test)] 38 | 39 | for i in range(M): 40 | # Fit a classifier with the specific weights 41 | weight = np.expand_dims(w, axis=1) 42 | 43 | clf.fit(X_train, Y_train, weight) 44 | pred_train_i = clf.predict(X_train) 45 | pred_test_i = clf.predict(X_test) 46 | # Indicator function 47 | miss = [int(x) for x in (pred_train_i != Y_train)] 48 | # Error 49 | err_m = np.dot(w, miss) / sum(w) 50 | # Alpha 51 | alpha_m = 0.5 * np.log((1 - err_m) / float(err_m+1e-15)) 52 | # New weights 53 | # print(miss[0], Y_train[0]) 54 | # quit() 55 | w = np.multiply(w, np.exp([float(pred_train_i[x]) * (-1) * Y_train[x] * alpha_m for x in range(len(miss))])) 56 | # Add to prediction 57 | pred_train = [sum(x) for x in zip(pred_train, 58 | [x * alpha_m for x in pred_train_i])] 59 | pred_test = [sum(x) for x in zip(pred_test, 60 | [x * alpha_m for x in pred_test_i])] 61 | 62 | pred_train, pred_test = np.sign(pred_train), np.sign(pred_test) 63 | # Return error rate in train and test set 64 | return get_error_rate(pred_train, Y_train), \ 65 | get_error_rate(pred_test, Y_test) 66 | 67 | 68 | """ PLOT FUNCTION ===========================================================""" 69 | 70 | 71 | def plot_error_rate(er_train, er_test): 72 | df_error = pd.DataFrame([er_train, er_test]).T 73 | df_error.columns = ['Training', 'Test'] 74 | plot1 = df_error.plot(linewidth=3, figsize=(8, 6), 75 | color=['lightblue', 'darkblue'], grid=True) 76 | plot1.set_xlabel('Number of iterations', fontsize=12) 77 | plot1.set_xticklabels(range(0, 50, 5)) 78 | plot1.set_ylabel('Error rate', fontsize=12) 79 | plot1.set_title('Error rate vs number of ensemble', fontsize=16) 80 | plt.axhline(y=er_test[0], linewidth=1, color='red', ls='dashed') 81 | plt.show() 82 | plt.savefig('./result.png') 83 | 84 | def load_dataset(): 85 | y = [] 86 | X = [] 87 | with open('./train.txt', 'r') as f: 88 | lines = f.readlines() 89 | for line in lines: 90 | line = line.strip().split(',') 91 | X.append(np.array([eval(x) for x in line[:4]])) 92 | y.append(eval(line[4])) 93 | X = np.array(X) 94 | y = np.array(y) 95 | 96 | X_test = [] 97 | y_test = [] 98 | with open('./test.txt', 'r') as f: 99 | lines = f.readlines() 100 | for line in lines: 101 | line = line.strip().split(',') 102 | X_test.append(np.array([eval(x) for x in line[:4]])) 103 | y_test.append(eval(line[4])) 104 | X_test = np.array(X_test) 105 | y_test = np.array(y_test) 106 | return X, y, X_test, y_test 107 | 108 | 109 | if __name__ == "__main__": 110 | 111 | X, y, X_test, y_test = load_dataset() 112 | 113 | clf = LogisticRegression() 114 | 115 | # Test with different number of iterations 116 | er_train, er_test = [], [] 117 | x_range = [1, 10, 25, 50] 118 | for i in x_range: 119 | print('The ensemble size is %s' % (i)) 120 | er_i = adaboost_clf(y, X, y_test, X_test, i, clf) 121 | # quit() 122 | er_train.append(er_i[0]) 123 | er_test.append(er_i[1]) 124 | 125 | # plot the error rate of trainset and testset 126 | plot_error_rate(er_train, er_test) -------------------------------------------------------------------------------- /5. Adaboost/logicalRegression.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | 3 | 4 | def sigmoid(x): 5 | return 1 / (1 + np.exp(-x)) 6 | 7 | class LogisticRegression(): 8 | """ 9 | Parameters: 10 | ----------- 11 | n_iterations: int 12 | 梯度下降的轮数 13 | learning_rate: float 14 | 梯度下降学习率 15 | """ 16 | def __init__(self, learning_rate=.1, n_iterations=90): 17 | self.learning_rate = learning_rate 18 | self.n_iterations = n_iterations 19 | 20 | def initialize_weights(self, n_features): 21 | # 初始化参数 22 | # 参数范围[-1/sqrt(N), 1/sqrt(N)] 23 | limit = np.sqrt(1 / n_features) 24 | w = np.random.uniform(-limit, limit, (n_features, 1)) 25 | b = 0 26 | self.w = np.insert(w, 0, b, axis=0) 27 | 28 | def fit(self, X, y, weight): 29 | m_samples, n_features = X.shape 30 | self.initialize_weights(n_features) 31 | # 为X增加一列特征x1,x1 = 0 32 | X = np.insert(X, 0, 1, axis=1) 33 | y = np.reshape(y, (m_samples, 1)) 34 | # weight = np.ones(shape=(67, 1)) + 1 35 | # 梯度训练n_iterations轮 36 | for i in range(self.n_iterations): 37 | h_x = X.dot(self.w) 38 | y_pred = sigmoid(h_x) 39 | # print(y.shape) 40 | # print((y_pred - y) * weight) 41 | # quit() 42 | w_grad = X.T.dot((y_pred - y) * weight) 43 | self.w = self.w - self.learning_rate * w_grad 44 | 45 | def predict(self, X): 46 | X = np.insert(X, 0, 1, axis=1) 47 | h_x = X.dot(self.w) 48 | y_pred = np.round([x[0] for x in sigmoid(h_x)]) 49 | return y_pred.astype(int) 50 | 51 | 52 | -------------------------------------------------------------------------------- /5. Adaboost/result.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhanzecheng/Implement_of_Statistical_learning/d708cac1d19e5d829918efb622f343bfa51020c3/5. Adaboost/result.png -------------------------------------------------------------------------------- /5. Adaboost/test.txt: -------------------------------------------------------------------------------- 1 | 3.6216,8.6661,-2.8073,-0.44699,0 2 | 4.5459,8.1674,-2.4586,-1.4621,0 3 | 3.866,-2.6383,1.9242,0.10645,0 4 | 3.4566,9.5228,-4.0112,-3.5944,0 5 | 0.32924,-4.4552,4.5718,-0.9888,0 6 | 4.3684,9.6718,-3.9606,-3.1625,0 7 | 3.5912,3.0129,0.72888,0.56421,0 8 | 2.0922,-6.81,8.4636,-0.60216,0 9 | 3.2032,5.7588,-0.75345,-0.61251,0 10 | 1.5356,9.1772,-2.2718,-0.73535,0 11 | 1.2247,8.7779,-2.2135,-0.80647,0 12 | 3.9899,-2.7066,2.3946,0.86291,0 13 | 1.8993,7.6625,0.15394,-3.1108,0 14 | -1.5768,10.843,2.5462,-2.9362,0 15 | 3.404,8.7261,-2.9915,-0.57242,0 16 | 4.6765,-3.3895,3.4896,1.4771,0 17 | 2.6719,3.0646,0.37158,0.58619,0 18 | 0.80355,2.8473,4.3439,0.6017,0 19 | 1.4479,-4.8794,8.3428,-2.1086,0 20 | 5.2423,11.0272,-4.353,-4.1013,0 21 | 1.4896,3.4288,-4.0309,-1.4259,1 22 | 0.11592,3.2219,-3.4302,-2.8457,1 23 | -3.3924,3.3564,-0.72004,-3.5233,1 24 | -6.1632,8.7096,-0.21621,-3.6345,1 25 | -4.0786,2.9239,0.87026,-0.65389,1 26 | -2.5899,-0.3911,0.93452,0.42972,1 27 | -1.0116,-0.19038,-0.90597,0.003003,1 28 | 0.066129,2.4914,-2.9401,-0.62156,1 29 | -0.24745,1.9368,-2.4697,-0.80518,1 30 | -1.5732,1.0636,-0.71232,-0.8388,1 31 | -2.1668,1.5933,0.045122,-1.678,1 32 | -1.1667,-1.4237,2.9241,0.66119,1 33 | -2.8391,-6.63,10.4849,-0.42113,1 34 | -4.5046,-5.8126,10.8867,-0.52846,1 35 | -2.41,3.7433,-0.40215,-1.2953,1 36 | 0.40614,1.3492,-1.4501,-0.55949,1 37 | -1.3887,-4.8773,6.4774,0.34179,1 38 | -3.7503,-13.4586,17.5932,-2.7771,1 39 | -3.5637,-8.3827,12.393,-1.2823,1 40 | -2.5419,-0.65804,2.6842,1.1952,1 41 | 5.7867,7.8902,-2.6196,-0.48708,0 42 | -0.24811,-0.17797,4.9068,0.15429,0 43 | 0.3292,-4.4552,4.5718,-0.9888,0 44 | 3.9362,10.1622,-3.8235,-4.0172,0 45 | 0.93584,8.8855,-1.6831,-1.6599,0 46 | 4.4338,9.887,-4.6795,-3.7483,0 47 | 0.7057,-5.4981,8.3368,-2.8715,0 48 | 1.1432,-3.7413,5.5777,-0.63578,0 49 | -0.38214,8.3909,2.1624,-3.7405,0 50 | 6.5633,9.8187,-4.4113,-3.2258,0 51 | -1.7479,-5.823,5.8699,1.212,1 52 | -0.95923,-6.7128,4.9857,0.32886,1 53 | 1.3451,0.23589,-1.8785,1.3258,1 54 | 2.2279,4.0951,-4.8037,-2.1112,1 55 | 1.2572,4.8731,-5.2861,-5.8741,1 56 | -5.3857,9.1214,-0.41929,-5.9181,1 57 | -2.9786,2.3445,0.52667,-0.40173,1 58 | -1.5851,-2.1562,1.7082,0.9017,1 59 | -0.21888,-2.2038,-0.0954,0.56421,1 60 | 1.3183,1.9017,-3.3111,0.065071,1 61 | -------------------------------------------------------------------------------- /5. Adaboost/train.txt: -------------------------------------------------------------------------------- 1 | 4.8906,-3.3584,3.4202,1.0905,0 2 | 1.4884,3.6274,3.308,0.48921,0 3 | 4.2969,7.617,-2.3874,-0.96164,0 4 | -0.96511,9.4111,1.7305,-4.8629,0 5 | -1.6162,0.80908,8.1628,0.60817,0 6 | 2.4391,6.4417,-0.80743,-0.69139,0 7 | 2.6881,6.0195,-0.46641,-0.69268,0 8 | 3.6289,0.81322,1.6277,0.77627,0 9 | 4.5679,3.1929,-2.1055,0.29653,0 10 | 3.4805,9.7008,-3.7541,-3.4379,0 11 | 4.1711,8.722,-3.0224,-0.59699,0 12 | -0.2062,9.2207,-3.7044,-6.8103,0 13 | -0.0068919,9.2931,-0.41243,-1.9638,0 14 | 0.96441,5.8395,2.3235,0.066365,0 15 | 2.8561,6.9176,-0.79372,0.48403,0 16 | -0.7869,9.5663,-3.7867,-7.5034,0 17 | 2.0843,6.6258,0.48382,-2.2134,0 18 | -0.7869,9.5663,-3.7867,-7.5034,0 19 | 3.9102,6.065,-2.4534,-0.68234,0 20 | 1.6349,3.286,2.8753,0.087054,0 21 | 4.3239,-4.8835,3.4356,-0.5776,0 22 | 5.262,3.9834,-1.5572,1.0103,0 23 | 3.1452,5.825,-0.51439,-1.4944,0 24 | 2.549,6.1499,-1.1605,-1.2371,0 25 | 4.9264,5.496,-2.4774,-0.50648,0 26 | 4.8265,0.80287,1.6371,1.1875,0 27 | 2.5635,6.7769,-0.61979,0.38576,0 28 | 5.807,5.0097,-2.2384,0.43878,0 29 | 3.1377,-4.1096,4.5701,0.98963,0 30 | -0.78289,11.3603,-0.37644,-7.0495,0 31 | 2.888,0.44696,4.5907,-0.24398,0 32 | 0.49665,5.527,1.7785,-0.47156,0 33 | 4.2586,11.2962,-4.0943,-4.3457,0 34 | 1.7939,-1.1174,1.5454,-0.26079,0 35 | 5.4021,3.1039,-1.1536,1.5651,0 36 | 2.5367,2.599,2.0938,0.20085,0 37 | 4.6054,-4.0765,2.7587,0.31981,0 38 | 2.4235,9.5332,-3.0789,-2.7746,0 39 | 1.0009,7.7846,-0.28219,-2.6608,0 40 | 0.12326,8.9848,-0.9351,-2.4332,0 41 | 3.9529,-2.3548,2.3792,0.48274,0 42 | 4.1373,0.49248,1.093,1.8276,0 43 | 4.7181,10.0153,-3.9486,-3.8582,0 44 | 4.1654,-3.4495,3.643,1.0879,0 45 | 4.4069,10.9072,-4.5775,-4.4271,0 46 | 2.3066,3.5364,0.57551,0.41938,0 47 | 3.7935,7.9853,-2.5477,-1.872,0 48 | 0.049175,6.1437,1.7828,-0.72113,0 49 | 0.24835,7.6439,0.9885,-0.87371,0 50 | 1.1317,3.9647,3.3979,0.84351,0 51 | 2.8033,9.0862,-3.3668,-1.0224,0 52 | 4.4682,2.2907,0.95766,0.83058,0 53 | 5.0185,8.5978,-2.9375,-1.281,0 54 | 1.8664,7.7763,-0.23849,-2.9634,0 55 | 3.245,6.63,-0.63435,0.86937,0 56 | 4.0296,2.6756,0.80685,0.71679,0 57 | -1.1313,1.9037,7.5339,1.022,0 58 | 0.87603,6.8141,0.84198,-0.17156,0 59 | 4.1197,-2.7956,2.0707,0.67412,0 60 | 3.8027,0.81529,2.1041,1.0245,0 61 | 1.4806,7.6377,-2.7876,-1.0341,0 62 | 4.0632,3.584,0.72545,0.39481,0 63 | 4.3064,8.2068,-2.7824,-1.4336,0 64 | 2.4486,-6.3175,7.9632,0.20602,0 65 | 3.2718,1.7837,2.1161,0.61334,0 66 | -0.64472,-4.6062,8.347,-2.7099,0 67 | 2.9543,1.076,0.64577,0.89394,0 68 | 2.1616,-6.8804,8.1517,-0.081048,0 69 | 3.82,10.9279,-4.0112,-5.0284,0 70 | -2.7419,11.4038,2.5394,-5.5793,0 71 | 3.3669,-5.1856,3.6935,-1.1427,0 72 | 4.5597,-2.4211,2.6413,1.6168,0 73 | 5.1129,-0.49871,0.62863,1.1189,0 74 | 3.3397,-4.6145,3.9823,-0.23751,0 75 | 4.2027,0.22761,0.96108,0.97282,0 76 | 3.5438,1.2395,1.997,2.1547,0 77 | 2.3136,10.6651,-3.5288,-4.7672,0 78 | -1.8584,7.886,-1.6643,-1.8384,0 79 | 3.106,9.5414,-4.2536,-4.003,0 80 | 2.9163,10.8306,-3.3437,-4.122,0 81 | 3.9922,-4.4676,3.7304,-0.1095,0 82 | 1.518,5.6946,0.094818,-0.026738,0 83 | 3.2351,9.647,-3.2074,-2.5948,0 84 | 4.2188,6.8162,-1.2804,0.76076,0 85 | 1.7819,6.9176,-1.2744,-1.5759,0 86 | 2.5331,2.9135,-0.822,-0.12243,0 87 | 3.8969,7.4163,-1.8245,0.14007,0 88 | 2.108,6.7955,-0.1708,0.4905,0 89 | 2.8969,0.70768,2.29,1.8663,0 90 | 0.9297,-3.7971,4.6429,-0.2957,0 91 | 3.4642,10.6878,-3.4071,-4.109,0 92 | 4.0713,10.4023,-4.1722,-4.7582,0 93 | -1.4572,9.1214,1.7425,-5.1241,0 94 | -1.5075,1.9224,7.1466,0.89136,0 95 | -0.91718,9.9884,1.1804,-5.2263,0 96 | 2.994,7.2011,-1.2153,0.3211,0 97 | -2.343,12.9516,3.3285,-5.9426,0 98 | 3.7818,-2.8846,2.2558,-0.15734,0 99 | 4.6689,1.3098,0.055404,1.909,0 100 | 3.4663,1.1112,1.7425,1.3388,0 101 | 3.2697,-4.3414,3.6884,-0.29829,0 102 | 5.1302,8.6703,-2.8913,-1.5086,0 103 | 2.0139,6.1416,0.37929,0.56938,0 104 | 0.4339,5.5395,2.033,-0.40432,0 105 | -1.0401,9.3987,0.85998,-5.3336,0 106 | 4.1605,11.2196,-3.6136,-4.0819,0 107 | 5.438,9.4669,-4.9417,-3.9202,0 108 | 5.032,8.2026,-2.6256,-1.0341,0 109 | 5.2418,10.5388,-4.1174,-4.2797,0 110 | -0.2062,9.2207,-3.7044,-6.8103,0 111 | 2.0911,0.94358,4.5512,1.234,0 112 | 1.7317,-0.34765,4.1905,-0.99138,0 113 | 4.1736,3.3336,-1.4244,0.60429,0 114 | 3.9232,-3.2467,3.4579,0.83705,0 115 | 3.8481,10.1539,-3.8561,-4.2228,0 116 | 0.5195,-3.2633,3.0895,-0.9849,0 117 | 3.8584,0.78425,1.1033,1.7008,0 118 | 1.7496,-0.1759,5.1827,1.2922,0 119 | 3.6277,0.9829,0.68861,0.63403,0 120 | 2.7391,7.4018,0.071684,-2.5302,0 121 | 4.5447,8.2274,-2.4166,-1.5875,0 122 | -1.7599,11.9211,2.6756,-3.3241,0 123 | 5.0691,0.21313,0.20278,1.2095,0 124 | 3.4591,11.112,-4.2039,-5.0931,0 125 | 1.9358,8.1654,-0.023425,-2.2586,0 126 | 2.486,-0.99533,5.3404,-0.15475,0 127 | 2.4226,-4.5752,5.947,0.21507,0 128 | 3.9479,-3.7723,2.883,0.019813,0 129 | 2.2634,-4.4862,3.6558,-0.61251,0 130 | 1.3566,4.2358,2.1341,0.3211,0 131 | 5.0452,3.8964,-1.4304,0.86291,0 132 | 3.5499,8.6165,-3.2794,-1.2009,0 133 | 0.17346,7.8695,0.26876,-3.7883,0 134 | 2.4008,9.3593,-3.3565,-3.3526,0 135 | 4.8851,1.5995,-0.00029081,1.6401,0 136 | 4.1927,-3.2674,2.5839,0.21766,0 137 | 1.1166,8.6496,-0.96252,-1.8112,0 138 | 1.0235,6.901,-2.0062,-2.7125,0 139 | -1.803,11.8818,2.0458,-5.2728,0 140 | 0.11739,6.2761,-1.5495,-2.4746,0 141 | 0.5706,-0.0248,1.2421,-0.5621,0 142 | 4.0552,-2.4583,2.2806,1.0323,0 143 | -1.6952,1.0657,8.8294,0.94955,0 144 | -1.1193,10.7271,2.0938,-5.6504,0 145 | 1.8799,2.4707,2.4931,0.37671,0 146 | 3.583,-3.7971,3.4391,-0.12501,0 147 | 0.19081,9.1297,-3.725,-5.8224,0 148 | 3.6582,5.6864,-1.7157,-0.23751,0 149 | -0.13144,-1.7775,8.3316,0.35214,0 150 | 2.3925,9.798,-3.0361,-2.8224,0 151 | 1.6426,3.0149,0.22849,-0.147,0 152 | -0.11783,-1.5789,8.03,-0.028031,0 153 | -0.69572,8.6165,1.8419,-4.3289,0 154 | 2.9421,7.4101,-0.97709,-0.88406,0 155 | -1.7559,11.9459,3.0946,-4.8978,0 156 | -1.2537,10.8803,1.931,-4.3237,0 157 | 3.2585,-4.4614,3.8024,-0.15087,0 158 | 1.8314,6.3672,-0.036278,0.049554,0 159 | 4.5645,-3.6275,2.8684,0.27714,0 160 | 2.7365,-5.0325,6.6608,-0.57889,0 161 | 0.9297,-3.7971,4.6429,-0.2957,0 162 | 3.9663,10.1684,-4.1131,-4.6056,0 163 | 1.4578,-0.08485,4.1785,0.59136,0 164 | 4.8272,3.0687,0.68604,0.80731,0 165 | -2.341,12.3784,0.70403,-7.5836,0 166 | -1.8584,7.886,-1.6643,-1.8384,0 167 | 4.1454,7.257,-1.9153,-0.86078,0 168 | 1.9157,6.0816,0.23705,-2.0116,0 169 | 4.0215,-2.1914,2.4648,1.1409,0 170 | 5.8862,5.8747,-2.8167,-0.30087,0 171 | -2.0897,10.8265,2.3603,-3.4198,0 172 | 4.0026,-3.5943,3.5573,0.26809,0 173 | -0.78689,9.5663,-3.7867,-7.5034,0 174 | 4.1757,10.2615,-3.8552,-4.3056,0 175 | 0.83292,7.5404,0.65005,-0.92544,0 176 | 4.8077,2.2327,-0.26334,1.5534,0 177 | 5.3063,5.2684,-2.8904,-0.52716,0 178 | 2.5605,9.2683,-3.5913,-1.356,0 179 | 2.1059,7.6046,-0.47755,-1.8461,0 180 | 2.1721,-0.73874,5.4672,-0.72371,0 181 | 4.2899,9.1814,-4.6067,-4.3263,0 182 | 3.5156,10.1891,-4.2759,-4.978,0 183 | 2.614,8.0081,-3.7258,-1.3069,0 184 | 0.68087,2.3259,4.9085,0.54998,0 185 | 4.1962,0.74493,0.83256,0.753,0 186 | 6.0919,2.9673,-1.3267,1.4551,0 187 | 1.3234,3.2964,0.2362,-0.11984,0 188 | 1.3264,1.0326,5.6566,-0.41337,0 189 | -0.16735,7.6274,1.2061,-3.6241,0 190 | -1.3,10.2678,-2.953,-5.8638,0 191 | -2.2261,12.5398,2.9438,-3.5258,0 192 | 2.4196,6.4665,-0.75688,0.228,0 193 | 1.0987,0.6394,5.989,-0.58277,0 194 | 4.6464,10.5326,-4.5852,-4.206,0 195 | -0.36038,4.1158,3.1143,-0.37199,0 196 | 1.3562,3.2136,4.3465,0.78662,0 197 | 0.5706,-0.0248,1.2421,-0.5621,0 198 | -2.6479,10.1374,-1.331,-5.4707,0 199 | 3.1219,-3.137,1.9259,-0.37458,0 200 | 5.4944,1.5478,0.041694,1.9284,0 201 | -2.5961,-9.349,9.7942,-0.28018,1 202 | -1.5228,-6.4789,5.7568,0.87325,1 203 | -0.53072,-0.097265,-0.21793,1.0426,1 204 | -0.49081,2.8452,-3.6436,-3.1004,1 205 | -6.5773,6.8017,0.85483,-7.5344,1 206 | -2.4621,2.7645,-0.62578,-2.8573,1 207 | -1.3995,-1.9162,2.5154,0.59912,1 208 | -2.3221,-9.3304,9.233,-0.79871,1 209 | -3.73,-12.9723,12.9817,-2.684,1 210 | -1.6988,-7.1163,5.7902,0.16723,1 211 | -0.26654,-0.64562,-0.42014,0.89136,1 212 | 0.33325,3.3108,-4.5081,-4.012,1 213 | -4.2091,4.7283,-0.49126,-5.2159,1 214 | -2.3142,-0.68494,1.9833,-0.44829,1 215 | -2.4835,-7.4494,6.8964,-0.64484,1 216 | -2.7611,-10.5099,9.0239,-1.9547,1 217 | -0.36025,-4.449,2.1067,0.94308,1 218 | 1.0117,0.9022,-2.3506,0.42714,1 219 | 0.96708,3.8426,-4.9314,-4.1323,1 220 | -5.2049,7.259,0.070827,-7.3004,1 221 | -3.3203,-0.02691,2.9618,-0.44958,1 222 | -2.565,-5.7899,6.0122,0.046968,1 223 | -1.5951,-6.572,4.7689,-0.94354,1 224 | 0.7049,0.17174,-1.7859,0.36119,1 225 | 1.7331,3.9544,-4.7412,-2.5017,1 226 | 0.6818,4.8504,-5.2133,-6.1043,1 227 | -6.3364,9.2848,0.014275,-6.7844,1 228 | -3.8053,2.4273,0.6809,-1.0871,1 229 | -2.1979,-2.1252,1.7151,0.45171,1 230 | -0.87874,-2.2121,-0.051701,0.099985,1 231 | 0.74067,1.7299,-3.1963,-0.1457,1 232 | 0.98296,3.4226,-3.9692,-1.7116,1 233 | -0.3489,3.1929,-3.4054,-3.1832,1 234 | -3.8552,3.5219,-0.38415,-3.8608,1 235 | -6.9599,8.9931,0.2182,-4.572,1 236 | -4.7462,3.1205,1.075,-1.2966,1 237 | -3.2051,-0.14279,0.97565,0.045675,1 238 | -1.7549,-0.080711,-0.75774,-0.3707,1 239 | -0.59587,2.4811,-2.8673,-0.89828,1 240 | -0.89542,2.0279,-2.3652,-1.2746,1 241 | -2.0754,1.2767,-0.64206,-1.2642,1 242 | -3.2778,1.8023,0.1805,-2.3931,1 243 | -2.2183,-1.254,2.9986,0.36378,1 244 | -3.5895,-6.572,10.5251,-0.16381,1 245 | -5.0477,-5.8023,11.244,-0.3901,1 246 | -3.5741,3.944,-0.07912,-2.1203,1 247 | -0.7351,1.7361,-1.4938,-1.1582,1 248 | -2.2617,-4.7428,6.3489,0.11162,1 249 | -4.244,-13.0634,17.1116,-2.8017,1 250 | -4.0218,-8.304,12.555,-1.5099,1 251 | -3.0201,-0.67253,2.7056,0.85774,1 252 | -2.4941,3.5447,-1.3721,-2.8483,1 253 | -0.83121,0.039307,0.05369,-0.23105,1 254 | -2.5665,-6.8824,7.5416,0.70774,1 255 | -4.4018,-12.9371,15.6559,-1.6806,1 256 | -3.7573,-8.2916,10.3032,0.38059,1 257 | -2.4725,-0.40145,1.4855,1.1189,1 258 | -1.9725,2.8825,-2.3086,-2.3724,1 259 | -2.0149,3.6874,-1.9385,-3.8918,1 260 | -0.82053,0.65181,-0.48869,-0.52716,1 261 | -1.7886,-6.3486,5.6154,0.42584,1 262 | -2.9138,-9.4711,9.7668,-0.60216,1 263 | -1.8343,-6.5907,5.6429,0.54998,1 264 | -0.8734,-0.033118,-0.20165,0.55774,1 265 | -0.70346,2.957,-3.5947,-3.1457,1 266 | -6.7387,6.9879,0.67833,-7.5887,1 267 | -2.7723,3.2777,-0.9351,-3.1457,1 268 | -1.6641,-1.3678,1.997,0.52283,1 269 | -2.4349,-9.2497,8.9922,-0.50001,1 270 | -3.793,-12.7095,12.7957,-2.825,1 271 | -1.9551,-6.9756,5.5383,-0.12889,1 272 | -0.69078,-0.50077,-0.35417,0.47498,1 273 | 0.025013,3.3998,-4.4327,-4.2655,1 274 | -4.3967,4.9601,-0.64892,-5.4719,1 275 | -2.456,-0.24418,1.4041,-0.45863,1 276 | -2.62,-6.8555,6.2169,-0.62285,1 277 | -2.9662,-10.3257,8.784,-2.1138,1 278 | -0.71494,-4.4448,2.2241,0.49826,1 279 | 0.6005,0.99945,-2.2126,0.097399,1 280 | 0.61652,3.8944,-4.7275,-4.3948,1 281 | -5.4414,7.2363,0.10938,-7.5642,1 282 | -3.5798,0.45937,2.3457,-0.45734,1 283 | -2.7769,-5.6967,5.9179,0.37671,1 284 | -1.8356,-6.7562,5.0585,-0.55044,1 285 | 0.30081,0.17381,-1.7542,0.48921,1 286 | 1.3403,4.1323,-4.7018,-2.5987,1 287 | 0.26877,4.987,-5.1508,-6.3913,1 288 | -6.5235,9.6014,-0.25392,-6.9642,1 289 | -4.0679,2.4955,0.79571,-1.1039,1 290 | -2.564,-1.7051,1.5026,0.32757,1 291 | -1.3414,-1.9162,-0.15538,-0.11984,1 292 | 0.23874,2.0879,-3.3522,-0.66553,1 293 | 0.6212,3.6771,-4.0771,-2.0711,1 294 | -0.77848,3.4019,-3.4859,-3.5569,1 295 | -4.1244,3.7909,-0.6532,-4.1802,1 296 | -7.0421,9.2,0.25933,-4.6832,1 297 | -4.9462,3.5716,0.82742,-1.4957,1 298 | -3.5359,0.30417,0.6569,-0.2957,1 299 | -2.0662,0.16967,-1.0054,-0.82975,1 300 | -0.88728,2.808,-3.1432,-1.2035,1 301 | -1.0941,2.3072,-2.5237,-1.4453,1 302 | -2.4458,1.6285,-0.88541,-1.4802,1 303 | -3.551,1.8955,0.1865,-2.4409,1 304 | -2.2811,-0.85669,2.7185,0.044382,1 305 | -3.6053,-5.974,10.0916,-0.82846,1 306 | -5.0676,-5.1877,10.4266,-0.86725,1 307 | -3.9204,4.0723,-0.23678,-2.1151,1 308 | -1.1306,1.8458,-1.3575,-1.3806,1 309 | -2.4561,-4.5566,6.4534,-0.056479,1 310 | -4.4775,-13.0303,17.0834,-3.0345,1 311 | -4.1958,-8.1819,12.1291,-1.6017,1 312 | -3.38,-0.7077,2.5325,0.71808,1 313 | -2.4365,3.6026,-1.4166,-2.8948,1 314 | -0.77688,0.13036,-0.031137,-0.35389,1 315 | -2.7083,-6.8266,7.5339,0.59007,1 316 | -4.5531,-12.5854,15.4417,-1.4983,1 317 | -3.8894,-7.8322,9.8208,0.47498,1 318 | -2.5084,-0.22763,1.488,1.2069,1 319 | -2.1652,3.0211,-2.4132,-2.4241,1 320 | -1.8974,3.5074,-1.7842,-3.8491,1 321 | -0.62043,0.5587,-0.38587,-0.66423,1 322 | -1.8387,-6.301,5.6506,0.19567,1 323 | -3,-9.1566,9.5766,-0.73018,1 324 | -1.9116,-6.1603,5.606,0.48533,1 325 | -1.005,0.084831,-0.2462,0.45688,1 326 | -0.87834,3.257,-3.6778,-3.2944,1 327 | -6.651,6.7934,0.68604,-7.5887,1 328 | -2.5463,3.1101,-0.83228,-3.0358,1 329 | -1.4377,-1.432,2.1144,0.42067,1 330 | -2.4554,-9.0407,8.862,-0.86983,1 331 | -3.9411,-12.8792,13.0597,-3.3125,1 332 | -2.1241,-6.8969,5.5992,-0.47156,1 333 | -0.74324,-0.32902,-0.42785,0.23317,1 334 | -0.071503,3.7412,-4.5415,-4.2526,1 335 | -4.2333,4.9166,-0.49212,-5.3207,1 336 | -2.3675,-0.43663,1.692,-0.43018,1 337 | -2.5526,-7.3625,6.9255,-0.66811,1 338 | -3.0986,-10.4602,8.9717,-2.3427,1 339 | -0.89809,-4.4862,2.2009,0.50731,1 340 | 0.56232,1.0015,-2.2726,-0.0060486,1 341 | 0.53936,3.8944,-4.8166,-4.3418,1 342 | -5.3012,7.3915,0.029699,-7.3987,1 343 | -3.3553,0.35591,2.6473,-0.37846,1 344 | -2.7908,-5.7133,5.953,0.45946,1 345 | -1.9983,-6.6072,4.8254,-0.41984,1 346 | 0.15423,0.11794,-1.6823,0.59524,1 347 | 1.208,4.0744,-4.7635,-2.6129,1 348 | 0.2952,4.8856,-5.149,-6.2323,1 349 | -6.4247,9.5311,0.022844,-6.8517,1 350 | -3.9933,2.6218,0.62863,-1.1595,1 351 | -2.659,-1.6058,1.3647,0.16464,1 352 | -1.4094,-2.1252,-0.10397,-0.19225,1 353 | 0.11032,1.9741,-3.3668,-0.65259,1 354 | 0.52374,3.644,-4.0746,-1.9909,1 355 | -0.76794,3.4598,-3.4405,-3.4276,1 356 | -3.9698,3.6812,-0.60008,-4.0133,1 357 | -7.0364,9.2931,0.16594,-4.5396,1 358 | -4.9447,3.3005,1.063,-1.444,1 359 | -3.5933,0.22968,0.7126,-0.3332,1 360 | -2.1674,0.12415,-1.0465,-0.86208,1 361 | -0.9607,2.6963,-3.1226,-1.3121,1 362 | -1.0802,2.1996,-2.5862,-1.2759,1 363 | -2.3277,1.4381,-0.82114,-1.2862,1 364 | -3.7244,1.9037,-0.035421,-2.5095,1 365 | -2.5724,-0.95602,2.7073,-0.16639,1 366 | -3.9297,-6.0816,10.0958,-1.0147,1 367 | -5.2943,-5.1463,10.3332,-1.1181,1 368 | -3.8953,4.0392,-0.3019,-2.1836,1 369 | -1.2244,1.7485,-1.4801,-1.4181,1 370 | -2.6406,-4.4159,5.983,-0.13924,1 371 | -4.6338,-12.7509,16.7166,-3.2168,1 372 | -4.2887,-7.8633,11.8387,-1.8978,1 373 | -3.3458,-0.50491,2.6328,0.53705,1 374 | -1.1188,3.3357,-1.3455,-1.9573,1 375 | 0.55939,-0.3104,0.18307,0.44653,1 376 | -1.5078,-7.3191,7.8981,1.2289,1 377 | -3.506,-12.5667,15.1606,-0.75216,1 378 | -2.9498,-8.273,10.2646,1.1629,1 379 | -1.6029,-0.38903,1.62,1.9103,1 380 | -1.2667,2.8183,-2.426,-1.8862,1 381 | -0.49281,3.0605,-1.8356,-2.834,1 382 | 0.66365,-0.045533,-0.18794,0.23447,1 383 | -0.72068,-6.7583,5.8408,0.62369,1 384 | -1.9966,-9.5001,9.682,-0.12889,1 385 | -0.97325,-6.4168,5.6026,1.0323,1 386 | -0.025314,-0.17383,-0.11339,1.2198,1 387 | 0.062525,2.9301,-3.5467,-2.6737,1 388 | -5.525,6.3258,0.89768,-6.6241,1 389 | -1.2943,2.6735,-0.84085,-2.0323,1 390 | -0.24037,-1.7837,2.135,1.2418,1 391 | -1.3968,-9.6698,9.4652,-0.34872,1 392 | -2.9672,-13.2869,13.4727,-2.6271,1 393 | -1.1005,-7.2508,6.0139,0.36895,1 394 | 0.22432,-0.52147,-0.40386,1.2017,1 395 | 0.90407,3.3708,-4.4987,-3.6965,1 396 | -2.8619,4.5193,-0.58123,-4.2629,1 397 | -1.0833,-0.31247,1.2815,0.41291,1 398 | -1.5681,-7.2446,6.5537,-0.1276,1 399 | -2.0545,-10.8679,9.4926,-1.4116,1 400 | 0.2346,-4.5152,2.1195,1.4448,1 401 | -------------------------------------------------------------------------------- /5. Adaboost/utils.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | # @Time : 2019/4/5 下午3:03 4 | # @Author : zhanzecheng 5 | # @File : utils.py 6 | # @Software: PyCharm 7 | """ 8 | from itertools import combinations_with_replacement 9 | import numpy as np 10 | import math 11 | 12 | def calculate_entropy(y): 13 | """ Calculate the entropy of label array y """ 14 | log2 = lambda x: math.log(x) / math.log(2) 15 | unique_labels = np.unique(y) 16 | entropy = 0 17 | for label in unique_labels: 18 | count = len(y[y == label]) 19 | p = count / len(y) 20 | entropy += -p * log2(p) 21 | return entropy 22 | 23 | 24 | def mean_squared_error(y_true, y_pred): 25 | """ Returns the mean squared error between y_true and y_pred """ 26 | mse = np.mean(np.power(y_true - y_pred, 2)) 27 | return mse 28 | 29 | 30 | def calculate_variance(X): 31 | """ Return the variance of the features in dataset X """ 32 | mean = np.ones(np.shape(X)) * X.mean(0) 33 | n_samples = np.shape(X)[0] 34 | variance = (1 / n_samples) * np.diag((X - mean).T.dot(X - mean)) 35 | 36 | return variance 37 | 38 | 39 | def calculate_std_dev(X): 40 | """ Calculate the standard deviations of the features in dataset X """ 41 | std_dev = np.sqrt(calculate_variance(X)) 42 | return std_dev 43 | 44 | 45 | def euclidean_distance(x1, x2): 46 | """ Calculates the l2 distance between two vectors """ 47 | distance = 0 48 | # Squared distance between each coordinate 49 | for i in range(len(x1)): 50 | distance += pow((x1[i] - x2[i]), 2) 51 | return math.sqrt(distance) 52 | 53 | 54 | def accuracy_score(y_true, y_pred): 55 | """ Compare y_true to y_pred and return the accuracy """ 56 | accuracy = np.sum(y_true == y_pred, axis=0) / len(y_true) 57 | return accuracy 58 | 59 | 60 | def calculate_covariance_matrix(X, Y=None): 61 | """ Calculate the covariance matrix for the dataset X """ 62 | if Y is None: 63 | Y = X 64 | n_samples = np.shape(X)[0] 65 | covariance_matrix = (1 / (n_samples - 1)) * (X - X.mean(axis=0)).T.dot(Y - Y.mean(axis=0)) 66 | 67 | return np.array(covariance_matrix, dtype=float) 68 | 69 | 70 | def calculate_correlation_matrix(X, Y=None): 71 | """ Calculate the correlation matrix for the dataset X """ 72 | if Y is None: 73 | Y = X 74 | n_samples = np.shape(X)[0] 75 | covariance = (1 / n_samples) * (X - X.mean(0)).T.dot(Y - Y.mean(0)) 76 | std_dev_X = np.expand_dims(calculate_std_dev(X), 1) 77 | std_dev_y = np.expand_dims(calculate_std_dev(Y), 1) 78 | correlation_matrix = np.divide(covariance, std_dev_X.dot(std_dev_y.T)) 79 | 80 | return np.array(correlation_matrix, dtype=float) 81 | 82 | def shuffle_data(X, y, seed=None): 83 | """ Random shuffle of the samples in X and y """ 84 | if seed: 85 | np.random.seed(seed) 86 | idx = np.arange(X.shape[0]) 87 | np.random.shuffle(idx) 88 | return X[idx], y[idx] 89 | 90 | 91 | def batch_iterator(X, y=None, batch_size=64): 92 | """ Simple batch generator """ 93 | n_samples = X.shape[0] 94 | for i in np.arange(0, n_samples, batch_size): 95 | begin, end = i, min(i + batch_size, n_samples) 96 | if y is not None: 97 | yield X[begin:end], y[begin:end] 98 | else: 99 | yield X[begin:end] 100 | 101 | 102 | def divide_on_feature(X, feature_i, threshold): 103 | """ Divide dataset based on if sample value on feature index is larger than 104 | the given threshold """ 105 | split_func = None 106 | if isinstance(threshold, int) or isinstance(threshold, float): 107 | split_func = lambda sample: sample[feature_i] >= threshold 108 | else: 109 | split_func = lambda sample: sample[feature_i] == threshold 110 | 111 | X_1 = np.array([sample for sample in X if split_func(sample)]) 112 | X_2 = np.array([sample for sample in X if not split_func(sample)]) 113 | 114 | return np.array([X_1, X_2]) 115 | 116 | 117 | def polynomial_features(X, degree): 118 | n_samples, n_features = np.shape(X) 119 | 120 | def index_combinations(): 121 | combs = [combinations_with_replacement(range(n_features), i) for i in range(0, degree + 1)] 122 | flat_combs = [item for sublist in combs for item in sublist] 123 | return flat_combs 124 | 125 | combinations = index_combinations() 126 | n_output_features = len(combinations) 127 | X_new = np.empty((n_samples, n_output_features)) 128 | 129 | for i, index_combs in enumerate(combinations): 130 | X_new[:, i] = np.prod(X[:, index_combs], axis=1) 131 | 132 | return X_new 133 | 134 | 135 | def get_random_subsets(X, y, n_subsets, replacements=True): 136 | """ Return random subsets (with replacements) of the data """ 137 | n_samples = np.shape(X)[0] 138 | # Concatenate x and y and do a random shuffle 139 | X_y = np.concatenate((X, y.reshape((1, len(y))).T), axis=1) 140 | np.random.shuffle(X_y) 141 | subsets = [] 142 | 143 | # Uses 50% of training samples without replacements 144 | subsample_size = int(n_samples // 2) 145 | if replacements: 146 | subsample_size = n_samples # 100% with replacements 147 | 148 | for _ in range(n_subsets): 149 | idx = np.random.choice( 150 | range(n_samples), 151 | size=np.shape(range(subsample_size)), 152 | replace=replacements) 153 | X = X_y[idx][:, :-1] 154 | y = X_y[idx][:, -1] 155 | subsets.append([X, y]) 156 | return subsets 157 | 158 | 159 | def normalize(X, axis=-1, order=2): 160 | """ Normalize the dataset X """ 161 | l2 = np.atleast_1d(np.linalg.norm(X, order, axis)) 162 | l2[l2 == 0] = 1 163 | return X / np.expand_dims(l2, axis) 164 | 165 | 166 | def standardize(X): 167 | """ Standardize the dataset X """ 168 | X_std = X 169 | mean = X.mean(axis=0) 170 | std = X.std(axis=0) 171 | for col in range(np.shape(X)[1]): 172 | if std[col]: 173 | X_std[:, col] = (X_std[:, col] - mean[col]) / std[col] 174 | # X_std = (X - X.mean(axis=0)) / X.std(axis=0) 175 | return X_std 176 | 177 | 178 | def train_test_split(X, y, test_size=0.5, shuffle=True, seed=None): 179 | """ Split the data into train and test sets """ 180 | if shuffle: 181 | X, y = shuffle_data(X, y, seed) 182 | # Split the training data from test data in the ratio specified in 183 | # test_size 184 | split_i = len(y) - int(len(y) // (1 / test_size)) 185 | X_train, X_test = X[:split_i], X[split_i:] 186 | y_train, y_test = y[:split_i], y[split_i:] 187 | 188 | return X_train, X_test, y_train, y_test 189 | 190 | 191 | def k_fold_cross_validation_sets(X, y, k, shuffle=True): 192 | """ Split the data into k sets of training / test data """ 193 | if shuffle: 194 | X, y = shuffle_data(X, y) 195 | 196 | n_samples = len(y) 197 | left_overs = {} 198 | n_left_overs = (n_samples % k) 199 | if n_left_overs != 0: 200 | left_overs["X"] = X[-n_left_overs:] 201 | left_overs["y"] = y[-n_left_overs:] 202 | X = X[:-n_left_overs] 203 | y = y[:-n_left_overs] 204 | 205 | X_split = np.split(X, k) 206 | y_split = np.split(y, k) 207 | sets = [] 208 | for i in range(k): 209 | X_test, y_test = X_split[i], y_split[i] 210 | X_train = np.concatenate(X_split[:i] + X_split[i + 1:], axis=0) 211 | y_train = np.concatenate(y_split[:i] + y_split[i + 1:], axis=0) 212 | sets.append([X_train, X_test, y_train, y_test]) 213 | 214 | # Add left over samples to last set as training samples 215 | if n_left_overs != 0: 216 | np.append(sets[-1][0], left_overs["X"], axis=0) 217 | np.append(sets[-1][2], left_overs["y"], axis=0) 218 | 219 | return np.array(sets) 220 | 221 | 222 | def to_categorical(x, n_col=None): 223 | """ One-hot encoding of nominal values """ 224 | if not n_col: 225 | n_col = np.amax(x) + 1 226 | one_hot = np.zeros((x.shape[0], n_col)) 227 | one_hot[np.arange(x.shape[0]), x] = 1 228 | return one_hot 229 | 230 | 231 | def to_nominal(x): 232 | """ Conversion from one-hot encoding to nominal """ 233 | return np.argmax(x, axis=1) 234 | 235 | 236 | def make_diagonal(x): 237 | """ Converts a vector into an diagonal matrix """ 238 | m = np.zeros((len(x), len(x))) 239 | for i in range(len(m[0])): 240 | m[i, i] = x[i] 241 | return m -------------------------------------------------------------------------------- /Optimization_method/1.Gradient_decent.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "### 这里我们使用梯度下降法来拟合房屋价格曲线" 8 | ] 9 | }, 10 | { 11 | "cell_type": "code", 12 | "execution_count": 1, 13 | "metadata": {}, 14 | "outputs": [], 15 | "source": [ 16 | "%matplotlib inline\n", 17 | "import numpy as np\n", 18 | "import matplotlib.pyplot as plt\n" 19 | ] 20 | }, 21 | { 22 | "cell_type": "code", 23 | "execution_count": 2, 24 | "metadata": { 25 | "scrolled": true 26 | }, 27 | "outputs": [ 28 | { 29 | "data": { 30 | "image/png": "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\n", 31 | "text/plain": [ 32 | "" 33 | ] 34 | }, 35 | "metadata": {}, 36 | "output_type": "display_data" 37 | } 38 | ], 39 | "source": [ 40 | "spaces = [45, 73, 89, 120, 140, 163]\n", 41 | "prices = [80, 150, 198, 230, 280, 360]\n", 42 | "spaces, prices = np.array(spaces), np.array(prices)\n", 43 | "plt.scatter(spaces, prices, c='g')\n", 44 | "plt.xlabel('house space')\n", 45 | "plt.ylabel('house price')\n", 46 | "plt.show()\n", 47 | "\n", 48 | "## 显示房屋面积和房屋价格的散点图" 49 | ] 50 | }, 51 | { 52 | "cell_type": "code", 53 | "execution_count": null, 54 | "metadata": {}, 55 | "outputs": [], 56 | "source": [] 57 | }, 58 | { 59 | "cell_type": "markdown", 60 | "metadata": {}, 61 | "source": [ 62 | "使用梯度下降法我们需要假设$ h(x)$\n", 63 | "这里我们假设 $$ h(x) = \\theta_0 + \\theta_1 * x \\tag{1}$$ (只含有一个特征)\n", 64 | "另损失函数为 $$ J(\\theta) = \\frac{1}{2*6}\\sum_{i=0}^{6}\\{(h_\\theta(X_i)-y_i)^{2} \\tag{2} $$\n", 65 | "假设步长为 $\\lambda$,则每一次的更新公式为:$$\\theta_j = \\theta_j - \\lambda * \\frac{1}{6}\\sum_{i=1}^6(h_\\lambda(X_i) - y_i)*X_{ij} \\tag{3} $$\n", 66 | "下面来用代码实现" 67 | ] 68 | }, 69 | { 70 | "cell_type": "code", 71 | "execution_count": 3, 72 | "metadata": {}, 73 | "outputs": [ 74 | { 75 | "name": "stdout", 76 | "output_type": "stream", 77 | "text": [ 78 | " h(x) = 0.016206 + 2.078464 * x\n" 79 | ] 80 | }, 81 | { 82 | "data": { 83 | "image/png": "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\n", 84 | "text/plain": [ 85 | "" 86 | ] 87 | }, 88 | "metadata": {}, 89 | "output_type": "display_data" 90 | } 91 | ], 92 | "source": [ 93 | "## theta 初始值\n", 94 | "theta0 = 0\n", 95 | "theta1 = 0\n", 96 | "\n", 97 | "## 如果步长选择不对,则 theta 参数更新结果会不对\n", 98 | "step = 0.00005\n", 99 | "\n", 100 | "x_i0 = np.ones((len(spaces)))\n", 101 | "\n", 102 | "# 假设函数\n", 103 | "def h(x) :\n", 104 | " return theta0 + theta1 * x\n", 105 | "\n", 106 | "# 损失函数\n", 107 | "def calc_error() :\n", 108 | " return np.sum(np.power((h(spaces) - prices),2)) / 6\n", 109 | "\n", 110 | "# 损失函数偏导数( theta 0)\n", 111 | "def calc_delta0() :\n", 112 | " return step * np.sum((h(spaces) - prices) * x_i0) / 6\n", 113 | "\n", 114 | "# 损失函数偏导数( theta 1)\n", 115 | "def calc_delta1() :\n", 116 | " return step * np.sum((h(spaces) - prices) * spaces) / 6\n", 117 | "\n", 118 | "# 循环更新 theta 值并计算误差,停止条件为\n", 119 | "# 1. 误差小于某个值\n", 120 | "# 2. 循环次数控制\n", 121 | "k = 0\n", 122 | "while True :\n", 123 | " delta0 = calc_delta0()\n", 124 | " delta1 = calc_delta1()\n", 125 | " theta0 = theta0 - delta0\n", 126 | " theta1 = theta1 - delta1\n", 127 | " error = calc_error()\n", 128 | " # print(\"delta [%f, %f], theta [%f, %f], error %f\" % (delta0, delta1, theta0, theta1, error))\n", 129 | " k = k + 1\n", 130 | " if (k > 10 or error < 200) : \n", 131 | " break\n", 132 | "\n", 133 | "\n", 134 | "print(\" h(x) = %f + %f * x\" % (theta0, theta1))\n", 135 | " \n", 136 | "# 使用假设函数计算出来的价格,用于画拟合曲线\n", 137 | "y_out = h(spaces)\n", 138 | "\n", 139 | "plt.scatter(spaces, prices, c='g')\n", 140 | "plt.plot(spaces, y_out, c='b')\n", 141 | "plt.xlabel('house space')\n", 142 | "plt.ylabel('house price')\n", 143 | "plt.show()" 144 | ] 145 | }, 146 | { 147 | "cell_type": "code", 148 | "execution_count": null, 149 | "metadata": {}, 150 | "outputs": [], 151 | "source": [] 152 | } 153 | ], 154 | "metadata": { 155 | "kernelspec": { 156 | "display_name": "Python 3", 157 | "language": "python", 158 | "name": "python3" 159 | }, 160 | "language_info": { 161 | "codemirror_mode": { 162 | "name": "ipython", 163 | "version": 3 164 | }, 165 | "file_extension": ".py", 166 | "mimetype": "text/x-python", 167 | "name": "python", 168 | "nbconvert_exporter": "python", 169 | "pygments_lexer": "ipython3", 170 | "version": "3.6.5" 171 | } 172 | }, 173 | "nbformat": 4, 174 | "nbformat_minor": 2 175 | } 176 | -------------------------------------------------------------------------------- /Optimization_method/2. 牛顿法.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "## 这一次介绍牛顿法,优点是二阶收敛,收敛快。缺点是需要求海森矩阵的逆,计算量大,且不一定有解" 8 | ] 9 | }, 10 | { 11 | "cell_type": "code", 12 | "execution_count": 1, 13 | "metadata": {}, 14 | "outputs": [], 15 | "source": [ 16 | "%matplotlib inline\n", 17 | "import numpy as np\n", 18 | "import matplotlib.pyplot as plt" 19 | ] 20 | }, 21 | { 22 | "cell_type": "markdown", 23 | "metadata": {}, 24 | "source": [ 25 | "牛顿法的核心便是选取极值点的临近点作为初始点,以此点切线于x轴的交点作为$x$的更新点,不断反复直至收敛,用公式来表示如下:$$ x_{k+1}=x_k - H^{-1}(x)\\bigtriangledown f(x)$$ \n", 26 | "其中$H^{-1}(x)$为海森矩阵\n", 27 | "选取函数 $$ y=100(x_2-x_1)^2+(1-x_1)^2 \\tag{1}$$\n", 28 | "于是可得函数的梯度$$g(x)=\\bigtriangledown f(x)=(-400(x_2 - x_1^2)x_1-2(1-x_1), 200(x_2-x_1^2))^T$$\n", 29 | "函数$f(x)$的Hesse矩阵为$$\n", 30 | "\\left \\{\\begin{matrix}\n", 31 | "-400(x_2-dx_1^2)+2 & -400x_1 \\\\\n", 32 | "-400x_1 & 200 \\\\\n", 33 | "\\end{matrix}\n", 34 | "\\right \\}\n", 35 | "$$" 36 | ] 37 | }, 38 | { 39 | "cell_type": "code", 40 | "execution_count": 6, 41 | "metadata": {}, 42 | "outputs": [], 43 | "source": [ 44 | "def jacobian(x):\n", 45 | " \"\"\"\n", 46 | " 返回函数的梯度\n", 47 | " \"\"\"\n", 48 | " return np.array([-400*x[0]*(x[1]-x[0]**2)-2*(1-x[0]),200*(x[1]-x[0]**2)])\n", 49 | "\n", 50 | "def hessian(x):\n", 51 | " \"\"\"\n", 52 | " 返回函数的海森矩阵\n", 53 | " \"\"\"\n", 54 | " return np.array([[-400*(x[1]-3*x[0]**2)+2,-400*x[0]],[-400*x[0],200]])" 55 | ] 56 | }, 57 | { 58 | "cell_type": "markdown", 59 | "metadata": {}, 60 | "source": [ 61 | "则现在开始实现函数" 62 | ] 63 | }, 64 | { 65 | "cell_type": "code", 66 | "execution_count": 11, 67 | "metadata": {}, 68 | "outputs": [], 69 | "source": [ 70 | "def newton(x0):\n", 71 | " \"\"\"\n", 72 | " 使用牛顿法来求极值\n", 73 | " \"\"\"\n", 74 | " print('初始点为:')\n", 75 | " print(x0,'\\n')\n", 76 | " W=np.zeros((2,10**3))\n", 77 | " i = 1\n", 78 | " imax = 1000\n", 79 | " W[:,0] = x0 \n", 80 | " x = x0\n", 81 | " # 定义容忍误差\n", 82 | " delta = 1\n", 83 | "\n", 84 | " while i10**(-5):\n", 85 | " p = -np.dot(np.linalg.inv(hessian(x)),jacobian(x))\n", 86 | " x0 = x\n", 87 | " x = x + p\n", 88 | " W[:,i] = x\n", 89 | " # 计算容忍误差\n", 90 | " delta = sum((x-x0)**2)\n", 91 | " print('第',i,'次迭代结果:')\n", 92 | " print(x,'\\n')\n", 93 | " i=i+1\n", 94 | " W=W[:,0:i] # 记录迭代点\n", 95 | " return W" 96 | ] 97 | }, 98 | { 99 | "cell_type": "code", 100 | "execution_count": 12, 101 | "metadata": {}, 102 | "outputs": [ 103 | { 104 | "name": "stdout", 105 | "output_type": "stream", 106 | "text": [ 107 | "初始点为:\n", 108 | "[-1.2 1. ] \n", 109 | "\n", 110 | "第 1 次迭代结果:\n", 111 | "[-1.1752809 1.38067416] \n", 112 | "\n", 113 | "第 2 次迭代结果:\n", 114 | "[ 0.76311487 -3.17503385] \n", 115 | "\n", 116 | "第 3 次迭代结果:\n", 117 | "[0.76342968 0.58282478] \n", 118 | "\n", 119 | "第 4 次迭代结果:\n", 120 | "[0.99999531 0.94402732] \n", 121 | "\n", 122 | "第 5 次迭代结果:\n", 123 | "[0.9999957 0.99999139] \n", 124 | "\n", 125 | "第 6 次迭代结果:\n", 126 | "[1. 1.] \n", 127 | "\n" 128 | ] 129 | }, 130 | { 131 | "data": { 132 | "image/png": "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\n", 133 | "text/plain": [ 134 | "" 135 | ] 136 | }, 137 | "metadata": {}, 138 | "output_type": "display_data" 139 | } 140 | ], 141 | "source": [ 142 | "X1=np.arange(-1.5,1.5+0.05,0.05)\n", 143 | "X2=np.arange(-3.5,2+0.05,0.05)\n", 144 | "[x1,x2]=np.meshgrid(X1,X2)\n", 145 | "f=100*(x2-x1**2)**2+(1-x1)**2; # 给定的函数\n", 146 | "plt.contour(x1,x2,f,20) # 画出函数的20条轮廓线\n", 147 | "x0 = np.array([-1.2,1])\n", 148 | "W=newton(x0)\n", 149 | "\n", 150 | "plt.plot(W[0,:],W[1,:],'g*',W[0,:],W[1,:]) # 画出迭代点收敛的轨迹\n", 151 | "plt.show()" 152 | ] 153 | }, 154 | { 155 | "cell_type": "code", 156 | "execution_count": null, 157 | "metadata": {}, 158 | "outputs": [], 159 | "source": [] 160 | }, 161 | { 162 | "cell_type": "code", 163 | "execution_count": null, 164 | "metadata": {}, 165 | "outputs": [], 166 | "source": [] 167 | } 168 | ], 169 | "metadata": { 170 | "kernelspec": { 171 | "display_name": "Python 3", 172 | "language": "python", 173 | "name": "python3" 174 | }, 175 | "language_info": { 176 | "codemirror_mode": { 177 | "name": "ipython", 178 | "version": 3 179 | }, 180 | "file_extension": ".py", 181 | "mimetype": "text/x-python", 182 | "name": "python", 183 | "nbconvert_exporter": "python", 184 | "pygments_lexer": "ipython3", 185 | "version": "3.6.5" 186 | } 187 | }, 188 | "nbformat": 4, 189 | "nbformat_minor": 2 190 | } 191 | -------------------------------------------------------------------------------- /Optimization_method/3.拟牛顿法.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "拟牛顿法的算法步骤如下:\n", 8 | "\n", 9 | "   1) 给出$x_0\\in R^n,H_0\\in R^{n*n},0\\le \\epsilon \\le1,k:=0$;\n", 10 | " \n", 11 | "   2) 若$\\bigtriangledown f(x^k)\\le \\epsilon$,迭代停止;否则求方向:$d_k=-J_k\\bigtriangledown f(x^k)$\n", 12 | " \n", 13 | "   3) 沿着方向做线性搜索$a_k>0$,令$x_{k+1}=x_{k}+a_kd_k$\n", 14 | " \n", 15 | "   4) 校正$H_K$产生$H_{k+1}$,使得牛顿条件成立\n", 16 | " \n", 17 | "   5) k=k+1,转第二步\n", 18 | "\n", 19 | "### 仅需一阶导数,就能完整整个迭代过程" 20 | ] 21 | }, 22 | { 23 | "cell_type": "code", 24 | "execution_count": null, 25 | "metadata": {}, 26 | "outputs": [], 27 | "source": [] 28 | }, 29 | { 30 | "cell_type": "code", 31 | "execution_count": null, 32 | "metadata": {}, 33 | "outputs": [], 34 | "source": [] 35 | } 36 | ], 37 | "metadata": { 38 | "kernelspec": { 39 | "display_name": "Python 3", 40 | "language": "python", 41 | "name": "python3" 42 | }, 43 | "language_info": { 44 | "codemirror_mode": { 45 | "name": "ipython", 46 | "version": 3 47 | }, 48 | "file_extension": ".py", 49 | "mimetype": "text/x-python", 50 | "name": "python", 51 | "nbconvert_exporter": "python", 52 | "pygments_lexer": "ipython3", 53 | "version": "3.6.5" 54 | } 55 | }, 56 | "nbformat": 4, 57 | "nbformat_minor": 2 58 | } 59 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Python3 代码实现《统计学习方法》 2 | 3 | ## 实现说明 4 | 5 | 6 | ## 实现进度 7 | 8 | | 模型 | 是否实现 | 9 | | ----------- | ---------------------------------------- | 10 | | 感知机 | 是 | 11 | | kd树-KNN | 是 | 12 | | 朴素贝叶斯方法 | 是 | 13 | | 决策树 | 是 | 14 | | Adaboost | 是 | 15 | 16 | 17 | 18 | | 优化方法 | 是否实现 | 19 | | ----------- | ---------------------------------------- | 20 | | 梯度下降 | 是 | 21 | | 牛顿法 | 是 | 22 | | 拟牛顿法 | 是 | 23 | -------------------------------------------------------------------------------- /RNN/LSTM.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "metadata": {}, 7 | "outputs": [ 8 | { 9 | "name": "stderr", 10 | "output_type": "stream", 11 | "text": [ 12 | "/usr/local/lib/python3.6/site-packages/h5py/__init__.py:36: FutureWarning: Conversion of the second argument of issubdtype from `float` to `np.floating` is deprecated. In future, it will be treated as `np.float64 == np.dtype(float).type`.\n", 13 | " from ._conv import register_converters as _register_converters\n" 14 | ] 15 | } 16 | ], 17 | "source": [ 18 | "%matplotlib inline\n", 19 | "import numpy as np\n", 20 | "import tensorflow as tf\n", 21 | "import matplotlib.pyplot as plt" 22 | ] 23 | }, 24 | { 25 | "cell_type": "code", 26 | "execution_count": 2, 27 | "metadata": {}, 28 | "outputs": [], 29 | "source": [ 30 | "num_steps = 10\n", 31 | "batch_size = 200\n", 32 | "num_classes = 2\n", 33 | "state_size = 16\n", 34 | "learning_rate = 0.1" 35 | ] 36 | }, 37 | { 38 | "cell_type": "code", 39 | "execution_count": 3, 40 | "metadata": {}, 41 | "outputs": [], 42 | "source": [ 43 | "def gen_data(size=1000000):\n", 44 | " X = np.array(np.random.choice(2, size=(size,)))\n", 45 | " Y = []\n", 46 | " '''根据规则生成Y'''\n", 47 | " for i in range(size): \n", 48 | " threshold = 0.5\n", 49 | " if X[i-3] == 1:\n", 50 | " threshold += 0.5\n", 51 | " if X[i-8] == 1:\n", 52 | " threshold -=0.25\n", 53 | " if np.random.rand() > threshold:\n", 54 | " Y.append(0)\n", 55 | " else:\n", 56 | " Y.append(1)\n", 57 | " return X, np.array(Y)\n", 58 | "\n", 59 | "\n", 60 | "'''生成batch数据'''\n", 61 | "def gen_batch(raw_data, batch_size, num_step):\n", 62 | " raw_x, raw_y = raw_data\n", 63 | " data_length = len(raw_x)\n", 64 | " batch_patition_length = data_length // batch_size # ->5000\n", 65 | " data_x = np.zeros([batch_size, batch_patition_length], dtype=np.int32) # ->(200, 5000)\n", 66 | " data_y = np.zeros([batch_size, batch_patition_length], dtype=np.int32) # ->(200, 5000)\n", 67 | " '''填到矩阵的对应位置'''\n", 68 | " for i in range(batch_size):\n", 69 | " data_x[i] = raw_x[batch_patition_length*i:batch_patition_length*(i+1)]# 每一行取batch_patition_length个数,即5000\n", 70 | " data_y[i] = raw_y[batch_patition_length*i:batch_patition_length*(i+1)]\n", 71 | " epoch_size = batch_patition_length // num_steps # ->5000/5=1000 就是每一轮的大小\n", 72 | " for i in range(epoch_size): # 抽取 epoch_size 个数据\n", 73 | " x = data_x[:, i * num_steps:(i + 1) * num_steps] # ->(200, 5)\n", 74 | " y = data_y[:, i * num_steps:(i + 1) * num_steps]\n", 75 | " yield (x, y) # yield 是生成器,生成器函数在生成值后会自动挂起并暂停他们的执行和状态(最后就是for循环结束后的结果,共有1000个(x, y))\n", 76 | "def gen_epochs(n, num_steps):\n", 77 | " for i in range(n):\n", 78 | " yield gen_batch(gen_data(), batch_size, num_steps)" 79 | ] 80 | }, 81 | { 82 | "cell_type": "code", 83 | "execution_count": 4, 84 | "metadata": {}, 85 | "outputs": [ 86 | { 87 | "name": "stdout", 88 | "output_type": "stream", 89 | "text": [ 90 | "----> numclass 2 16\n" 91 | ] 92 | } 93 | ], 94 | "source": [ 95 | "\n", 96 | "'''定义placeholder'''\n", 97 | "x = tf.placeholder(tf.int32, [batch_size, num_steps], name=\"x\")\n", 98 | "y = tf.placeholder(tf.int32, [batch_size, num_steps], name='y')\n", 99 | "init_state = tf.zeros([batch_size, state_size])\n", 100 | "init_c = tf.zeros([batch_size, state_size])\n", 101 | "'''RNN输入'''\n", 102 | "x_one_hot = tf.one_hot(x, num_classes)\n", 103 | "rnn_inputs = tf.unstack(x_one_hot, axis=1)\n", 104 | "\n", 105 | "print('----> numclass', num_classes, state_size)\n", 106 | "'''定义RNN cell'''\n", 107 | "# Input gate: input, previous output, and bias.\n", 108 | "ix = tf.Variable(tf.truncated_normal([num_classes, state_size], -0.1, 0.1))\n", 109 | "im = tf.Variable(tf.truncated_normal([state_size, state_size], -0.1, 0.1))\n", 110 | "ib = tf.Variable(tf.zeros([1, state_size]))\n", 111 | "# Forget gate: input, previous output, and bias.\n", 112 | "fx = tf.Variable(tf.truncated_normal([num_classes, state_size], -0.1, 0.1))\n", 113 | "fm = tf.Variable(tf.truncated_normal([state_size, state_size], -0.1, 0.1))\n", 114 | "fb = tf.Variable(tf.zeros([1, state_size]))\n", 115 | "# Memory cell: input, state and bias. \n", 116 | "cx = tf.Variable(tf.truncated_normal([num_classes, state_size], -0.1, 0.1))\n", 117 | "cm = tf.Variable(tf.truncated_normal([state_size, state_size], -0.1, 0.1))\n", 118 | "cb = tf.Variable(tf.zeros([1, state_size]))\n", 119 | "# Output gate: input, previous output, and bias.\n", 120 | "ox = tf.Variable(tf.truncated_normal([num_classes, state_size], -0.1, 0.1))\n", 121 | "om = tf.Variable(tf.truncated_normal([state_size, state_size], -0.1, 0.1))\n", 122 | "ob = tf.Variable(tf.zeros([1, state_size]))\n", 123 | "# Variables saving state across unrollings.\n" 124 | ] 125 | }, 126 | { 127 | "cell_type": "code", 128 | "execution_count": 5, 129 | "metadata": {}, 130 | "outputs": [], 131 | "source": [ 132 | "def lstm_cell(rnn_input, h, c):\n", 133 | " # a = tf.matmul(rnn_input, ix)\n", 134 | " # print('--a', a.get_shape())\n", 135 | " # a = tf.matmul(h, im)\n", 136 | " # print('--a', a.get_shape())\n", 137 | " input_gate = tf.sigmoid(tf.matmul(rnn_input, ix) + tf.matmul(h, im) + ib)\n", 138 | " forget_gate = tf.sigmoid(tf.matmul(rnn_input, fx) + tf.matmul(h, fm) + fb)\n", 139 | " update = tf.matmul(rnn_input, cx) + tf.matmul(c, cm) + cb\n", 140 | " state = forget_gate * c + input_gate * tf.tanh(update)\n", 141 | " output_gate = tf.sigmoid(tf.matmul(rnn_input, ox) + tf.matmul(h, om) + ob)\n", 142 | " return output_gate * tf.tanh(state), state\n" 143 | ] 144 | }, 145 | { 146 | "cell_type": "code", 147 | "execution_count": 6, 148 | "metadata": {}, 149 | "outputs": [ 150 | { 151 | "name": "stdout", 152 | "output_type": "stream", 153 | "text": [ 154 | "----> fianl state (200, 16)\n" 155 | ] 156 | } 157 | ], 158 | "source": [ 159 | "state = init_state\n", 160 | "c = init_c\n", 161 | "rnn_outputs = []\n", 162 | "for rnn_input in rnn_inputs:\n", 163 | " state, c = lstm_cell(rnn_input, state, c) # state会重复使用,循环\n", 164 | " rnn_outputs.append(state)\n", 165 | "final_state = rnn_outputs[-1] # 得到最后的state\n", 166 | "print('----> fianl state', final_state.get_shape())\n", 167 | "\n", 168 | "# cell = tf.contrib.rnn.BasicRNNCell(num_units=state_size)\n", 169 | "# rnn_outputs, final_state = tf.contrib.rnn.static_rnn(cell=cell, inputs=rnn_inputs,\n", 170 | "# initial_state=init_state)\n", 171 | "# rnn_outputs, final_state = tf.nn.dynamic_rnn(cell=cell, inputs=rnn_inputs,\n", 172 | "# initial_state=init_state)\n", 173 | "\n", 174 | "\n", 175 | "'''预测,损失,优化'''\n", 176 | "with tf.variable_scope('softmax'):\n", 177 | " W = tf.get_variable('W', [state_size, num_classes])\n", 178 | " b = tf.get_variable('b', [num_classes], initializer=tf.constant_initializer(0.0))\n", 179 | "logits = [tf.matmul(rnn_output, W) + b for rnn_output in rnn_outputs]\n", 180 | "predictions = [tf.nn.softmax(logit) for logit in logits]\n", 181 | "\n", 182 | "y_as_list = tf.unstack(y, num=num_steps, axis=1)\n", 183 | "losses = [tf.nn.sparse_softmax_cross_entropy_with_logits(labels=label, logits=logit) for logit, label in\n", 184 | " zip(logits, y_as_list)]\n", 185 | "total_loss = tf.reduce_mean(losses)\n", 186 | "train_step = tf.train.AdagradOptimizer(learning_rate).minimize(total_loss)" 187 | ] 188 | }, 189 | { 190 | "cell_type": "code", 191 | "execution_count": null, 192 | "metadata": {}, 193 | "outputs": [], 194 | "source": [] 195 | } 196 | ], 197 | "metadata": { 198 | "kernelspec": { 199 | "display_name": "Python 3", 200 | "language": "python", 201 | "name": "python3" 202 | }, 203 | "language_info": { 204 | "codemirror_mode": { 205 | "name": "ipython", 206 | "version": 3 207 | }, 208 | "file_extension": ".py", 209 | "mimetype": "text/x-python", 210 | "name": "python", 211 | "nbconvert_exporter": "python", 212 | "pygments_lexer": "ipython3", 213 | "version": "3.6.5" 214 | } 215 | }, 216 | "nbformat": 4, 217 | "nbformat_minor": 2 218 | } 219 | -------------------------------------------------------------------------------- /RNN/RNN.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "metadata": {}, 7 | "outputs": [ 8 | { 9 | "name": "stderr", 10 | "output_type": "stream", 11 | "text": [ 12 | "/usr/local/lib/python3.6/site-packages/h5py/__init__.py:36: FutureWarning: Conversion of the second argument of issubdtype from `float` to `np.floating` is deprecated. In future, it will be treated as `np.float64 == np.dtype(float).type`.\n", 13 | " from ._conv import register_converters as _register_converters\n" 14 | ] 15 | } 16 | ], 17 | "source": [ 18 | "%matplotlib inline\n", 19 | "import numpy as np\n", 20 | "import tensorflow as tf\n", 21 | "import matplotlib.pyplot as plt" 22 | ] 23 | }, 24 | { 25 | "cell_type": "markdown", 26 | "metadata": {}, 27 | "source": [ 28 | "接下来定义超参数" 29 | ] 30 | }, 31 | { 32 | "cell_type": "code", 33 | "execution_count": 2, 34 | "metadata": {}, 35 | "outputs": [], 36 | "source": [ 37 | "num_steps = 10\n", 38 | "batch_size = 200\n", 39 | "num_classes = 2\n", 40 | "state_size = 16\n", 41 | "learning_rate = 0.1" 42 | ] 43 | }, 44 | { 45 | "cell_type": "markdown", 46 | "metadata": {}, 47 | "source": [ 48 | "接下来生成数据" 49 | ] 50 | }, 51 | { 52 | "cell_type": "code", 53 | "execution_count": 3, 54 | "metadata": {}, 55 | "outputs": [], 56 | "source": [ 57 | "def gen_data(size=1000000):\n", 58 | " X = np.array(np.random.choice(2, size=(size,)))\n", 59 | " Y = []\n", 60 | " '''根据规则生成Y'''\n", 61 | " for i in range(size): \n", 62 | " threshold = 0.5\n", 63 | " if X[i-3] == 1:\n", 64 | " threshold += 0.5\n", 65 | " if X[i-8] == 1:\n", 66 | " threshold -=0.25\n", 67 | " if np.random.rand() > threshold:\n", 68 | " Y.append(0)\n", 69 | " else:\n", 70 | " Y.append(1)\n", 71 | " return X, np.array(Y)\n", 72 | "\n", 73 | "\n", 74 | "'''生成batch数据'''\n", 75 | "def gen_batch(raw_data, batch_size, num_step):\n", 76 | " raw_x, raw_y = raw_data\n", 77 | " data_length = len(raw_x)\n", 78 | " batch_patition_length = data_length // batch_size # ->5000\n", 79 | " data_x = np.zeros([batch_size, batch_patition_length], dtype=np.int32) # ->(200, 5000)\n", 80 | " data_y = np.zeros([batch_size, batch_patition_length], dtype=np.int32) # ->(200, 5000)\n", 81 | " '''填到矩阵的对应位置'''\n", 82 | " for i in range(batch_size):\n", 83 | " data_x[i] = raw_x[batch_patition_length*i:batch_patition_length*(i+1)]# 每一行取batch_patition_length个数,即5000\n", 84 | " data_y[i] = raw_y[batch_patition_length*i:batch_patition_length*(i+1)]\n", 85 | " epoch_size = batch_patition_length // num_steps # ->5000/5=1000 就是每一轮的大小\n", 86 | " for i in range(epoch_size): # 抽取 epoch_size 个数据\n", 87 | " x = data_x[:, i * num_steps:(i + 1) * num_steps] # ->(200, 5)\n", 88 | " y = data_y[:, i * num_steps:(i + 1) * num_steps]\n", 89 | " yield (x, y) # yield 是生成器,生成器函数在生成值后会自动挂起并暂停他们的执行和状态(最后就是for循环结束后的结果,共有1000个(x, y))\n", 90 | "def gen_epochs(n, num_steps):\n", 91 | " for i in range(n):\n", 92 | " yield gen_batch(gen_data(), batch_size, num_steps)" 93 | ] 94 | }, 95 | { 96 | "cell_type": "markdown", 97 | "metadata": {}, 98 | "source": [ 99 | "接下来定义网络结构" 100 | ] 101 | }, 102 | { 103 | "cell_type": "code", 104 | "execution_count": 4, 105 | "metadata": {}, 106 | "outputs": [], 107 | "source": [ 108 | "# 先定义placeholder\n", 109 | "x = tf.placeholder(tf.int32, [batch_size, num_steps], name='x')\n", 110 | "y = tf.placeholder(tf.int32, [batch_size, num_steps], name='y')\n", 111 | "init_state = tf.zeros([batch_size, state_size], name='init_state')\n", 112 | "'''RNN输入'''\n", 113 | "x_one_hot = tf.one_hot(x, num_classes)\n", 114 | "rnn_inputs = tf.unstack(x_one_hot, axis=1)" 115 | ] 116 | }, 117 | { 118 | "cell_type": "markdown", 119 | "metadata": {}, 120 | "source": [ 121 | "这里rnn_cell里面的W和b代表着共用着同一个(就是在时间步下来是共享参数的),这也就是为什么要分开定义rnn_cell和w、b" 122 | ] 123 | }, 124 | { 125 | "cell_type": "code", 126 | "execution_count": 5, 127 | "metadata": {}, 128 | "outputs": [], 129 | "source": [ 130 | "'''接下来定义RNN_cell'''\n", 131 | "with tf.variable_scope('rnn_cell'):\n", 132 | " W = tf.get_variable('W', [num_classes + state_size, state_size]) # 这里其实是 [W, U]\n", 133 | " b = tf.get_variable('b', [state_size], initializer=tf.constant_initializer(0.0))\n", 134 | "\n", 135 | "# 从这里开始写RNN的公式,当然tf可以自动算梯度\n", 136 | "def rnn_cell(rnn_input, state):\n", 137 | " with tf.variable_scope('rnn_cell', reuse=True):\n", 138 | " W = tf.get_variable('W', [num_classes + state_size, state_size])\n", 139 | " b = tf.get_variable('b', [state_size], initializer=tf.constant_initializer(0.0))\n", 140 | " return tf.tanh(tf.matmul(tf.concat([rnn_input, state], axis=1), W) + b)\n", 141 | "\n", 142 | " " 143 | ] 144 | }, 145 | { 146 | "cell_type": "markdown", 147 | "metadata": {}, 148 | "source": [ 149 | "需要注意的是这里的init_state十分重要" 150 | ] 151 | }, 152 | { 153 | "cell_type": "code", 154 | "execution_count": 6, 155 | "metadata": {}, 156 | "outputs": [], 157 | "source": [ 158 | "'''这里开始做循环的操作'''\n", 159 | "state = init_state\n", 160 | "rnn_outputs = []\n", 161 | "for rnn_input in rnn_inputs:\n", 162 | " state = rnn_cell(rnn_input, state)\n", 163 | " rnn_outputs.append(state)" 164 | ] 165 | }, 166 | { 167 | "cell_type": "code", 168 | "execution_count": 7, 169 | "metadata": {}, 170 | "outputs": [], 171 | "source": [ 172 | "with tf.variable_scope('project'):\n", 173 | " # 这一块是吧多个神经元映射到了一个上面\n", 174 | " W = tf.get_variable('W', [state_size, num_classes])\n", 175 | " b = tf.get_variable('b', [num_classes], initializer=tf.constant_initializer(0.0))\n", 176 | "\n", 177 | "logits = [tf.matmul(rnn_output, W) + b for rnn_output in rnn_outputs]\n", 178 | "# 接下来接一个softmax层\n", 179 | "pred = [tf.nn.softmax(logit) for logit in logits]\n" 180 | ] 181 | }, 182 | { 183 | "cell_type": "code", 184 | "execution_count": 8, 185 | "metadata": {}, 186 | "outputs": [], 187 | "source": [ 188 | "y_as_list = tf.unstack(y, num=num_steps, axis=1)\n", 189 | "losses = [tf.nn.sparse_softmax_cross_entropy_with_logits(labels=label,logits=logit) for logit, label in zip(logits, y_as_list)]\n", 190 | "total_loss = tf.reduce_mean(losses)\n", 191 | "train_step = tf.train.AdagradOptimizer(learning_rate).minimize(total_loss)\n" 192 | ] 193 | }, 194 | { 195 | "cell_type": "code", 196 | "execution_count": null, 197 | "metadata": {}, 198 | "outputs": [], 199 | "source": [] 200 | } 201 | ], 202 | "metadata": { 203 | "kernelspec": { 204 | "display_name": "Python 3", 205 | "language": "python", 206 | "name": "python3" 207 | }, 208 | "language_info": { 209 | "codemirror_mode": { 210 | "name": "ipython", 211 | "version": 3 212 | }, 213 | "file_extension": ".py", 214 | "mimetype": "text/x-python", 215 | "name": "python", 216 | "nbconvert_exporter": "python", 217 | "pygments_lexer": "ipython3", 218 | "version": "3.6.5" 219 | } 220 | }, 221 | "nbformat": 4, 222 | "nbformat_minor": 2 223 | } 224 | --------------------------------------------------------------------------------