机器学习实战-logistic回归分类
基于LR的回归分类实例
概念
前提理解:
机器学习的三个步骤:模型,损失函数(即样本误差),优化求解(通过损失函数,使得模型的样本误差最小或小于阈值,求出满足条件的参数,优化求解包括:最小二乘法,梯度下降)。
链接1:简析python3深浅复制与赋值
https://cloud.tencent.com/developer/news/53299
Python3中赋值操作其实是对象的引用,相当于起了个别名,赋值关系,即整个内外层对象的引用,内外层都指向同一内存。
链接2:SGD详解
https://www.cnblogs.com/NeilZhang/p/8454890.html
实例说明
基于LR的回归分类实例,特征抽取使用TF-IDF,模型优化采用SGD.
模型:LR模型
损失函数:均方误差
优化求解:SGD,迭代50次
输入:TF-IDF的词频
输出:0,1
数据集样例
train目录:
【train_neg.txt】
不要 怕 恶庄 自寻 棺材 睡
走 咯 拜拜
大家 说 说 找操 科技 ( 兆 日 科技 ) 这波 能 跌 到 多少
每 一次 反弹 都 是 逃命 的 机会 不要 抱 任何 幻想
抛 ! 机构 连拉 股价 制造 概念 用 人性 贪婪 忽悠 小股民 在 高位 接货 333
垃圾股
18 71 , , 20000 股 跌 吧 黑庄
兆 日 科技 跌停 控股 股东 拟 大幅 减持
今天 是 老子 买入 你整 半年 时间 狗日 的 … … 27 3
【train_pos.txt】
兆 日 科技 抄 家伙 满仓 买进 干
哇 日日 大涨 了 呀 呵呵 呵 吻 啊 日日 使劲
前来 入住
涨停 涨停 涨停 涨停 涨停 涨停 涨停 涨停 涨停 涨停 涨停 涨停 涨停
今天 涨停
兆 跌 科技 有望 了
我 预测 这是 最后 一波 诱空 洗盘 该股 下跌 过程 中日 线 和 周线 的 kdj 均 未 创新 低 一旦 反弹
19 65 老夫 已 满仓 买入 坐等 拉升 哈哈 !
差不多 了 再 跌进 不了 深港 通 了 60 亿 底线 市值
主力 拉高 再 出货 没 子弹 就 说
老子 还要 持股 过节
有 涨停 就 有 跌停 所以 很 正常 周一 拉红 是 肯定 的 星期二 的 操作 就要 多方面 考虑 了
便宜货 我 先拣点
test目录同train目录
【stopwords.txt】
你 等 我 吗 在 股价 股市 科技 股友 兆日 最后 今天 明天 公告 一个 还 已 发行价 做 月 到 还是 得 估计 谁 啊 可能 的 可以 来 。 。。 , ! 了 的 地 ? 就是 应该 明天 下午 上午 早上 晚上 你 等 我 吗 在 股价 股市 科技 股友 兆日 ——— 》), )÷(1- ", )、 =( : → ℃ & * 一一 ~~~~ ' . 『 .一 ./ -- 』 =″ 【 [*] }> [⑤]] [①D] c] ng昉 * // [ ] [②e] [②g] ={ } ,也 ' A [①⑥] [②B] [①a] [④a] [①③] [③h] ③] 1. -- [②b] '' ××× [①⑧] 0:2 =[ [⑤b] [②c] [④b] [②③] [③a] [④c] [①⑤] [①⑦] [①g] ∈[ [①⑨] [①④] [①c] [②f] [②⑧] [②①] [①C] [③c] [③g] [②⑤] [②②] 一. [①h] .数 [] [①B] 数/ [①i] [③e] [①①] [④d] [④e] [③b] [⑤a] [①A] [②⑧] [②⑦] [①d] [②j] 〕〔 ][ :// ′∈ [②④ [⑤e] 12% b] ... ................... …………………………………………………③ ZXFITL [③F] 」 [①o] ]∧′=[ ∪φ∈ ′| {- ②c } [③①] R.L. [①E] Ψ -[*]- ↑ .日 [②d] [② [②⑦] [②②] [③e] [①i] [①B] [①h] [①d] [①g] [①②] [②a] f] [⑩] a] [①e] [②h] [②⑥] [③d] [②⑩] e] 〉 】 元/吨 [②⑩] 2.3% 5:0 [①] :: [②] [③] [④] [⑤] [⑥] [⑦] [⑧] [⑨] …… —— ? 、 。 " " 《 》 ! , : ; ? . , . ' ? · ——— ── ? — < > ( ) 〔 〕 [ ] ( ) - + ~ × / / ① ② ③ ④ ⑤ ⑥ ⑦ ⑧ ⑨ ⑩ Ⅲ В " ; # @ γ μ φ φ. × Δ ■ ▲ sub exp sup sub Lex # % & ' + +ξ ++ - -β < <± <Δ <λ <φ << = = =☆ =- > >λ _ ~± ~+ [⑤f] [⑤d] [②i] ≈ [②G] [①f] LI ㈧ [- ...... 〉 [③⑩] 第二 一番 一直 一个 一些 许多 种 有的是 也就是说 末##末 啊 阿 哎 哎呀 哎哟 唉 俺 俺们 按 按照 吧 吧哒 把 罢了 被 本 本着 比 比方 比如 鄙人 彼 彼此 边 别 别的 别说 并 并且 不比 不成 不单 不但 不独 不管 不光 不过 不仅 不拘 不论 不怕 不然 不如 不特 不惟 不问 不只 朝 朝着 趁 趁着 乘 冲 除 除此之外 除非 除了 此 此间 此外 从 从而 打 待 但 但是 当 当着 到 得 的 的话 等 等等 地 第 叮咚 对 对于 多 多少 而 而况 而且 而是 而外 而言 而已 尔后 反过来 反过来说 反之 非但 非徒 否则 嘎 嘎登 该 赶 个 各 各个 各位 各种 各自 给 根据 跟 故 故此 固然 关于 管 归 果然 果真 过 哈 哈哈 呵 和 何 何处 何况 何时 嘿 哼 哼唷 呼哧 乎 哗 还是 还有 换句话说 换言之 或 或是 或者 极了 及 及其 及至 即 即便 即或 即令 即若 即使 几 几时 己 既 既然 既是 继而 加之 假如 假若 假使 鉴于 将 较 较之 叫 接着 结果 借 紧接着 进而 尽 尽管 经 经过 就 就是 就是说 据 具体地说 具体说来 开始 开外 靠 咳 可 可见 可是 可以 况且 啦 来 来着 离 例如 哩 连 连同 两者 了 临 另 另外 另一方面 论 嘛 吗 慢说 漫说 冒 么 每 每当 们 莫若 某 某个 某些 拿 哪 哪边 哪儿 哪个 哪里 哪年 哪怕 哪天 哪些 哪样 那 那边 那儿 那个 那会儿 那里 那么 那么些 那么样 那时 那些 那样 乃 乃至 呢 能 你 你们 您 宁 宁可 宁肯 宁愿 哦 呕 啪达 旁人 呸 凭 凭借 其 其次 其二 其他 其它 其一 其余 其中 起 起见 起见 岂但 恰恰相反 前后 前者 且 然而 然后 然则 让 人家 任 任何 任凭 如 如此 如果 如何 如其 如若 如上所述 若 若非 若是 啥 上下 尚且 设若 设使 甚而 甚么 甚至 省得 时候 什么 什么样 使得 是 是的 首先 谁 谁知 顺 顺着 似的 虽 虽然 虽说 虽则 随 随着 所 所以 他 他们 他人 它 它们 她 她们 倘 倘或 倘然 倘若 倘使 腾 替 通过 同 同时 哇 万一 往 望 为 为何 为了 为什么 为着 喂 嗡嗡 我 我们 呜 呜呼 乌乎 无论 无宁 毋宁 嘻 吓 相对而言 像 向 向着 嘘 呀 焉 沿 沿着 要 要不 要不然 要不是 要么 要是 也 也罢 也好 一 一般 一旦 一方面 一来 一切 一样 一则 依 依照 矣 以 以便 以及 以免 以至 以至于 以致 抑或 因 因此 因而 因为 哟 用 由 由此可见 由于 有 有的 有关 有些 又 于 于是 于是乎 与 与此同时 与否 与其 越是 云云 哉 再说 再者 在 在下 咱 咱们 则 怎 怎么 怎么办 怎么样 怎样 咋 照 照着 者 这 这边 这儿 这个 这会儿 这就是说 这里 这么 这么点儿 这么些 这么样 这时 这些 这样 正如 吱 之 之类 之所以 之一 只是 只限 只要 只有 至 至于 诸位 着 着呢 自 自从 自个儿 自各儿 自己 自家 自身 综上所述 总的来看 总的来说 总的说来 总而言之 总之 纵 纵令 纵然 纵使 遵照 作为 兮 呃 呗 咚 咦 喏 啐 喔唷 嗬 嗯 嗳
附代码
#coding=utf-8 ''' 基于LR的回归分类实例,特征抽取使用IF-IDF,模型优化采用SGD. 模型:LR模型 损失函数:均方误差 优化求解:SGD,迭代50次 输入:IF-IDF的词频 输出:0,1 ''' import os import math import random class LR_Uni_Bi: def __init__(self, train_dir, test_dir, alpha=0.01): #初始化,设置文件目录及算法学习速率 self.train_dir = train_dir self.test_dir = test_dir self.alpha = alpha #字典 self.dic ={} def loadStopWords(self): for line in open('./stopwords.txt'): doc = line.split() # print(doc) #集合(set)是一个无序的不重复元素序列 self.stopwords = set(doc) # print(self.stopwords) def buildUnigram(self,min1=0,sw1 =True): ''' 定义一元词袋模型 ''' #临时变量,存储unigram的次数,用于min-count过滤 temp_dic = {} for fname in os.listdir(self.train_dir): for line in open(os.path.join(self.train_dir, fname)): for token in line.split(): if token not in temp_dic: temp_dic[token]=1 else: temp_dic[token] +=1 #临时变量,存储过滤后的词语 temp_set = set() for word in temp_dic: if temp_dic[word] > min1: temp_set.add(word) if sw1: self.loadStopWords() #两个集合求差集,对数据进行过滤 temp_set -= self.stopwords count = 0 for word in temp_set: self.dic[word] = count count += 1 print('unigram',len(self.dic)) def buildBigram(self,min2 = 5, sw2 =True): ''' 构造二元词袋模型,以词的个数为标准,而不是词的长度。 ('一段时间', '主力') ''' #临时变量,用于存储bigram的次数,用于min-count过滤 self.gram2 ={} for fname in os.listdir(self.train_dir): for line in open(os.path.join(self.train_dir,fname)): doc = line.split() # print(doc) for i in range(len(doc) - 1): t = tuple(doc[i:i+2]) if t not in self.gram2: self.gram2[t] = 1 else: self.gram2[t] += 1 # print(self.gram2) #python中单引号和双引号组合使用,来避免使用转义字符 print('original bigram', len(self.gram2)) remove_set = set() for g in self.gram2: if self.gram2[g] <= min2: remove_set.add(g) if sw2: if g[0] in self.stopwords and g[1] in self.stopwords: remove_set.add(g) for g in remove_set: del self.gram2[g] print('bigram min-count -%d %d'%(min2,len(self.gram2))) #经过一元词袋模型后,当前字典的纬度,表示有效的unigram的个数 self.uni_count = len(self.dic) count = self.uni_count for g in self.gram2: self.dic[g] =count count += 1 print('bigram',len(self.dic) - self.uni_count) # print(self.dic) def buildDic(self, min1 = 0, min2 = 0 , sw1 = True,sw2 = True): #构建一词模型和两词模型 self.buildUnigram(min1=min1,sw1=sw1) self.buildBigram(min2=min2,sw2=sw2) def getlabel(self): ''' 提取训练数据的标签 ''' self.train_label = [ ] for fname in os.listdir(self.train_dir): if fname == 'train_neg.txt': label = 0 else: label = 1 for line in open(os.path.join(self.train_dir,fname)): self.train_label.append(label) # print(self.train_label) self.test_label = [] for fname in os.listdir(self.test_dir): if fname =='test_neg.txt': label = 0 else: label = 1 for line in open(os.path.join(self.test_dir,fname)): self.test_label.append(label) # print(self.test_label) def setLog(self,log_dir): #设置日志数据的文件目录 self.log_dir = log_dir #训练数据日志 self.fw_train = open(self.log_dir + '/train_log.txt','w') #测试数据日志 self.fw_test = open(self.log_dir + '/test_log.txt','w') def buildDocsTFIDF(self,dir): #文件词频 idf={} docs = [] #unigram对应的各个文档的有效长度 docs_length1 = [] #bigram对应的各个文件的有效长度 docs_length2 = [] for fname in os.listdir(dir): num = 0 for line in open(os.path.join(dir,fname)): # print(docs) docs.append({}) # print(docs) doc = line.split() count1 = 0 count2 = 0 temp_set =set() for word in doc: if word in self.dic: idx = self.dic[word] # print(idx) count1 += 1 temp_set.add(idx) # print(temp_set) if idx not in docs[-1]: docs[-1][idx] = 1 else: docs[-1][idx] += 1 # print(docs) for i in range(len(doc) - 1): t = tuple(doc[i:i+2]) if t in self.dic: count2 +=1 idx = self.dic[t] temp_set.add(idx) if idx not in docs[-1]: docs[-1][idx] = 1 else: docs[-1][idx] += 1 #统计包含词w的文档数目 for idx in temp_set: if idx not in idf: idf[idx] = 1 else: idf[idx] += 1 docs_length1.append(count1) docs_length2.append(count2) #语料库中的文档总数 N = len(docs)+0.0 for idx in idf: idf[idx] = math.log(N / idf[idx]) # print(docs) #计算词频-逆向文件频率 for i in range(len(docs)): #赋值操作,相当于另起别名,实质是整个内外层对象的引用。详细参考python3的深浅拷贝和赋值 doc = docs[i] # print(doc) # print(docs[i]) for idx in doc: if idx<self.uni_count: doc[idx] = doc[idx] / (docs_length1[i] + 0.0)* idf[idx] else: doc[idx] = doc[idx] / (docs_length2[i] + 0.0)* idf[idx] # print(doc) # print(docs[i]) # exit() # print(docs) # num += 1 # if num ==2: # exit() # for doc in docs: # print(doc) # exit() return docs def initTheta(self): ''' 随机初始化theta ''' self.theta = [] for i in range(len(self.dic)): self.theta.append(random.random()) def sigmoid(self,x): ''' sigmoid function ''' return 1.0/(1+math.exp(-x)) def SGD(self, iter, train_f, test_f): ''' Stochastic Gradient Descent ''' #随机初始化theta self.initTheta() #start SGD for j in range(iter): sample = random.sample(range(len(train_f)), len(train_f)) for i in sample: thetaX = 0 x = train_f[i] for idx in x: thetaX += self.theta[idx] * x[idx] #LR回归求解预测值,Loggstic Regression h = self.sigmoid(thetaX) #损失函数的求导步骤求误差,用于迭代跟新thetaX error = self.train_label[i] - h #SGD更新迭代跟新参数thetaX for idx in x: self.theta[idx] = self.theta[idx]+ (self.alpha*error*x[idx]) print('iter %d' % j) print('alpha',self.alpha) test_acc = self.test(train_f,test_f) print('test_acc',test_acc) def test(self,train_f, test_f): ''' 测试 ''' correct = 0 for i in range(len(train_f)): x = train_f[i] thetaX = 0 for idx in x: thetaX += self.theta[idx] * x[idx] h = self.sigmoid(thetaX) #临时变量,暂存预测的文章的类型 y = 0 if h > 0.5: y = 1 #统计预测正确的数目 if y == self.train_label[i]: correct += 1 #计算预测的准确值 train_acc = correct /(len(train_f) + 0.0) print('6-1 training acc', train_acc) self.fw_train.write(str(train_acc)) self.fw_train.write('\n') correct = 0 for i in range(len(test_f)): x = test_f[i] thetaX = 0 for idx in x: thetaX += self.theta[idx] * x[idx] h = self.sigmoid(thetaX) y = 0 if h > 0.5: y = 1 if y == self.test_label[i]: correct += 1 test_acc = correct /(len(test_f) + 0.0) print('6-1 test acc', test_acc) self.fw_test.write(str(test_acc)) self.fw_test.write('\n') return test_acc def closeFw(self): self.fw_test.close() self.fw_train.close() def writeGramTable(self): ''' 输出保存词表及其权重 ''' self.fw_grams = open(self.log_dir + '/words.txt', 'w') gram_weight = {} for g in self.dic: #判断对象的变量类型 if isinstance(g,tuple): str = g[0]+' ' +g[1] else: str =g gram_weight[str] = self.theta[self.dic[g]] sort = sorted(gram_weight.items(), key=lambda e: e[1], reverse=False) #按值排序 for(gram, weight) in sort: self.fw_grams.write(gram) self.fw_grams.write(' ') self.fw_grams.write('%.3f' %weight) self.fw_grams.write('\n') self.fw_grams.close() def writeResults(self, test_f): ''' 输出分类结果 ''' self.fw_res = open(self.log_dir+'results.txt','w') for i in range(len(test_f)): x = test_f[i] thetaX = 0 for idx in x: thetaX += self.theta[idx] * x[idx] h= self.sigmoid(thetaX) y = 0 if h>0.5: y = 1 self.fw_res.write('%d' % y) self.fw_res.write(' ') self.fw_res.write('%d' % self.test_label[i]) self.fw_res.write(' ') if y == self.test_label[i]: self.fw_res.write('y') else: self.fw_res.write('n') self.fw_res.write('\n') self.fw_res.close() def truncateTest(self,threshold,test_f): ''' 截取一些小权重的词,进行测试 ''' correct = 0 for i in range(len(test_f)): x = test_f[i] thetaX = 0 for idx in x: if abs(self.theta[idx]) >= threshold: thetaX += self.theta[idx] * x[idx] h = self.sigmoid(thetaX) y = 0 if h>0.5: y = 1 if y == self.test_label[i]: correct += 1 test_acc = correct / (len(self.test_docs) + 0.0) return test_acc def SGDwithTFIDF(self,iter): ''' 特征抽取 用IFIDF做特征的stochastic Gradient Descent (HashingTF and IDF)词频-逆向文件频率,体现一个文档中词语对于语料库的重要程度。 ''' self.train_docs = self.buildDocsTFIDF(self.train_dir) # print(self.train_docs) print('train TFIDF',len(self.train_docs)) self.test_docs = self.buildDocsTFIDF(self.test_dir) # print('test TFIDF',len(self.test_docs)) # exit() #将TFIDF值进行SGD模型优化求解 self.SGD(iter,self.train_docs,self.test_docs) #关闭文件流 self.closeFw() #输出保存词表和权重 self.writeGramTable() #输出分类结果 self.writeResults(self.test_docs) ''' 截取一小段进行测试 ''' for i in range(600): threshold = i/600.0*40.0 print('truncate threshold %f acc %f' %(threshold,self.truncateTest(threshold, self.test_docs))) if __name__ == '__main__': #数据初始化,设置输入路径 lr = LR_Uni_Bi('./train', './test', alpha=0.5) #训练数据,特征变化,构造一元和二元语言模型 lr.buildDic(min1=0,min2=0,sw1=True,sw2=True) #有监督训练,提取训练数据标签 lr.getlabel() #设置输出路径 lr.setLog('./out') #模型构建 lr.SGDwithTFIDF(iter= 50)