懵懂的菜鸟

Stay hungry,Stay foolish.

导航

机器学习实战-logistic回归分类

基于LR的回归分类实例

概念

前提理解:

机器学习的三个步骤:模型,损失函数(即样本误差),优化求解(通过损失函数,使得模型的样本误差最小或小于阈值,求出满足条件的参数,优化求解包括:最小二乘法,梯度下降)。

 

链接1:简析python3深浅复制与赋值

https://cloud.tencent.com/developer/news/53299

Python3中赋值操作其实是对象的引用,相当于起了个别名,赋值关系,即整个内外层对象的引用,内外层都指向同一内存。

链接2SGD详解

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)

 

posted on 2019-05-10 23:53  懵懂的菜鸟  阅读(340)  评论(0编辑  收藏  举报