高考小强源码阅读

news/2024/11/27 9:55:41/

读别人的代码像破解密码一样,乐趣无穷。
高考小强是一个基于Python2的、关于高考志愿填报的问答系统,它收集了往年的各个学校、各个专业的分数信息,可以根据省份、分数、文理推荐合适的学校。

一、五大板块

  • CollegeRecommendation
    学校、专业推荐模块,通过SQL查询数据库获得推荐的学校列表、专业列表
  • DialogueManagement
    对话管理模块,将格式化数据转化为具体的句子
  • InformationExtraction
    查询抽取模块
  • InformationRecognition
    查询识别模块
  • 主调用模块
    调用其他模块

不同模块之间以JSON的形式传递数据。

二、DialogueManagement:对话管理模块

本模块是离用户最近的模块,包含两个类:

  • ReadOrWriteWithConsole:控制台读入写出类,涉及到UTF8编码的转换,这个类可有可无,不太重要。
  • Response类,这个类是本模块的核心,下面重点介绍这个类。

Response类有如下几个静态函数,每个函数都表示高考小强能说出的一类话。

  • initial_ask():初始的问候语
  • normal_inquire_response(collegelist):正常询问回答,传入参数为collegelist,即推荐的学校列表
  • normal_major_response(major_list):把推荐的专业列表告知用户,“据小强分析,可以考虑的报考方向或专业有”
  • re_ask_lack_attribute(lack_tag):缺少属性回答,lack_tag表示缺少的属性,用户必须提供省份、文理、分数等信息,如果没有提供全,就要再次询问用户
  • more_restriction():是否有更多限制,比如用户说“我想去北京上大学”,就要限制为学校是北京的大学。
  • could_to_some_college(tag, college, now_type):能否上某某大学。
  • ambiguous_school(base, school_list):模糊学校,比如用户说“我想上东大”,无法判断是东南大学还是东北大学
  • i_donnot_know():小强认怂,“抱歉,小强的数据不够充分,暂时不能预测”
  • what_function():返回功能简介。比如用户问“有什么功能”。
  • too_big_score():“您的分数太高了,吹牛不是好习惯”

下面详细介绍各个函数。

initial_ask()

为了假装自己很灵活,写了5个问候语,随机选一个。这种方法在本系统中大量使用。
打印完问候语之后,就要开始干活了:问问用户的省份、文理、分数。

    def initial_ask():seed = random.randint(0, 4)re = []随机选取5个问候句if seed == 0:re.append(u'您好,我是人工智能小强,专注于高考志愿填报')elif seed == 1:re.append(u'您好,我是高考志愿填报助手小强')elif seed == 2:re.append(u'很高兴见到你,我叫小强')elif seed == 3:re.append(u'Hello!我是小强!')elif seed == 4:re.append(u'高考志愿填报助手--小强,竭诚为您服务!')re.append(u'小强是根据往年数据,结合分数与排名为您推荐学校,推荐结果仅供参考!')re.append(u'请问您有什么和高考志愿填报相关的需求?')re.append(u'输入省份、分数和文理即可开始挑选学校啦!')return re

normal_inquire_response(collegelist)

collegelist是一个如下结构的JSON

{'low':[('东北大学',628,'本科提前批'),('华中科技',630,'本科提前批')],'mid':[('北航',650,'本科提前批'),('西安交大',649,'本科提前批')],'high':[('清华大学',680,'本科提前批')]
}

此函数的作用就是把这个学校列表转化为一个字符串告知用户。

re_ask_lack_attribute(lack_tag)

lack_tag可能的取值:

  • origin:省份
  • type:文理
  • score:分数

根据缺失的属性,小强会问你“您的[省份|文理|分数]是什么?”,此函数又是写了多个问法模板随机选一个,以避免让人觉得死板。

more_restriction():还有其他要求没有

    def more_restriction():seed = random.randint(0, 1)re = u''if seed == 0:re = u'请问还有什么其他要求吗?'elif seed == 1:re = u'还需要做什么筛选吗?'return [re]

could_to_some_college(tag, college, now_type)

此函数用于回答“我能不能上北大”这样的问题。
tag表示小强的观点,分为四类:

  • 完全可以
  • 有把握
  • 很有可能
  • 不可能

college表示用户想要上的学校,now_type表示专业,这两个参数都是在拼接回复的时候用到。

ambiguous_school(base, school_list)

base=‘东大’,school_list=['东北大学','东南大学']

三、CollegeRecommendation模块:数据发生的地方

DialogueManagement只是将结构化数据转化为文本,没做什么大事。
InformatioonExtractor和InformationRecognition只是解析用户输入,也没做什么大事。
CollegeRecommendation模块则是系统的核心,是真正涉及到数据处理的地方。
本系统使用的是MySQL数据库,CollegeRecommendation的作用就是执行SQL语句去数据库里面查询。

下面首先介绍一下数据库设计。

分数名次表

score_rank表的结构

  • origin:省份
  • type:文理
  • year:年份
  • score:分数
  • rank:名次

分数-学校表

  • origin:省份
  • type:文理
  • year:年份
  • average_score:平均分
  • min_score:最低分
  • min_rank:最低分全省名次
  • batch:批次

分数-专业表

  • origin
  • type
  • year
  • average_score
  • major
  • school
  • batch

饭得一口一口吃,事得一件一件做。先看recommend_school

predict_school(origin, type, score,school)

判断能不能上某学校
参数: origin:省份(不含"省"字,如"山东""新疆""西藏"), type:"文科"或 "理科", score:分数, school:学校名
返回值:
{'result':0/1/2/3(基本不可能/可能性较小/有把握/太亏),'school_score':学校预测平均分, 'school_rank':学校最低分排名, 'student_rank':学生排名},各项若为-1则是缺少数据,数据不足以做出判断则返回None

根据分数、省份、文理获取省内排名
select rank from score_rank where origin = %s and type = %s and year = 2016 order by abs(score - %s) limit 1 ',(origin,type,score)

根据省份、文理、学校、年份获得学校的平均分、最低分。

select average_score,min_score,min_rank from school_score where student_origin = %s and student_type = %s and school = %s and year = 2016',(origin,type,school)

高考小强观点的产生,根据学校的平均分、最低分综合判断

    # 先根据线上分判断if sch_ave_score > 0 :if score < sch_ave_score - 15 :result = 0elif score < sch_ave_score - 5 :result = 1elif score < sch_ave_score + 5 :result = 2else :result = 3# 再根据最低分判断,会覆盖线上分结果if sch_min_score > 0 :if score < sch_min_score :result = 0elif score < sch_min_score + 10 :result = 1elif score < sch_min_score + 20:result = 2else :result = 3

recommend_school_rank(origin, type, score)

# 参数: origin:省份(不含"省"字,如"山东""新疆""西藏"), type:"文科"或 "理科", score:分数
# 返回值:((保底学校1,保底学校2...),(推荐学校1,推荐学校2...),(冲一冲学校1,冲一冲学校2...)); 学校信息包括(学校名,预测分数,批次)

此函数返回五个学校列表,每个学校是一个三元组(学校名称,平均分,批次)

  • 太亏,预测最低分在考生分数-20以下
  • 保底学校,预测最低分在考生分数-20到-10的学校
  • 推荐学校,预测最低分在考生分数-10到0的学校
  • 冲一冲学校,预测最低分在考生分数+0到+10的学校
  • 不可能,预测最低分在考生分数+10以上

这五种情况的SQL语句都很相似,不同之处在于min_score分数不同。

select school,average_score,batch 
from school_score 
where year = 2016 and student_origin = "%s" and student_type = "%s" and min_score > %d + 10' % (origin, type, score)

recommend_school_answer(origin,type,score)

这个函数返回值是str类型的。这个函数主要用来进行单元测试。

再来看recommend_major.py
本模块有一个major.txt,里面是各个专业的名称缩写。

init_major_list()

初始化专业列表,将数据库中各个专业和major.txt中的缩写对应起来。

predict_major_fullname(origin, type, score,school,major)

给定省份、文理、分数、年份,判断能不能上某学校的某专业。
原理就是查询score_major表,得到该学校该专业的分数,根据分数差分为4个等级,来表达小强的态度。


参数:origin:省份(不含"省"字,如"山东""新疆""西藏"),type:"文科"或 "理科", score:分数, school:学校名, major:专业全称
返回值:{'result':0/1/2/3(基本不可能/可能性较小/有把握/太亏), 'major_score':预测平均分},各项若为-1则是缺少数据,数据不足以做出判断则返回None

predict_major(origin, type, score, school, major)

这个函数是上面predict_major_fullname()的包装,它首先获取专业简写对应的全部专业,然后调用predict_major_fullname()函数。

# 判断能不能上某学校专业
# 参数: origin:省份(不含"省"字,如"山东""新疆""西藏"), type:"文科"或 "理科", score:分数, school:学校名, major:专业简称
# 返回值:{专业全称:{'result':0/1/2/3(基本不可能/可能性较小/有把握/太亏), 'major_score':预测平均分}},每个全称对应一条结果,若结果为空则该学校无对应专业,各项若为-1则是缺少数据,数据不足以做出判断则返回None
def predict_major(origin, type, score, school, major) :r = {}if major in majors.keys() :major_full = majors[major]for m in major_full :r[m] = predict_major_fullname(origin,type,score,school,m)else :return Nonereturn r

recommend_school_fullname(origin, type, score, major)

根据省份、分数、专业推荐学校。

# 参数: origin:省份(不含"省"字,如"山东""新疆""西藏"), type:"文科"或 "理科", score:分数, major:专业全称
# 返回值:{保底学校:学校列表,推荐学校:学校列表,冲一冲学校:学校列表
}

实现就是SQL语句,三类学校分数差不同。这跟recommend_school.py中直接推荐学校很相似。

select school,average_score,batch from major_score where year = 2016 and student_origin = "%s" and student_type = "%s" and major = "%s" and average_score > %d + 5 and average_score < %d + 15' % (origin, type, major, score, score)

recommend_major(origin,type,score,major)

用户查询的major是简写的专业名称,一个专业简写对应多个全名专业。
根据专业推荐学校,分两步:

  • 把简写的专业进行扩展,得到一个专业列表
  • 对于专业列表中的每一个专业推荐学校
def recommend_major(origin,type_in,score,major) :r = {}if major in majors:major_full = majors[major]for m in major_full :r[m] = recommend_school_fullname(origin,type_in,score,m)else :return Nonereturn r

StateTracking模块:用户状态变化

相关文件

  • _type.py:定义了一些枚举
  • state.py:定义了状态变化

先看_type.py中的枚举,需要说明的是Python2实现枚举比较麻烦,Python3中枚举变得非常简单了。


def my_enum(**enums):return type('Enum', (), enums)StateType = my_enum(INIT = 0,ENOUGH_BASIC_INFO = 1,ASK_BACK_FOR_SOMETHING = 2,TO_INIT = 3,
)IntentType = my_enum(NORMAL_INQUIRE = 1,HOW_ABOUT = 2,ASK_FUNCTION = 4,)AskType = my_enum(NONE = 0,ORIGIN = 1,TYPE = 2,SCORE = 3,AMBIGUOUS = 4,
)

这三个枚举非常重要,是理解整个系统的重要入口。
用户状态枚举:开始、足够信息、信息不全
用户意图枚举:正常询问、怎么样(“我能上清华大学吗”)、询问功能(“这个系统怎么用啊”)
用户信息枚举:用来记录用户当前提供了哪些信息,包括什么信息也没有、有了省份信息、有了文理信息、有了分数信息等。

state.py定义的是上下文信息。
首先定义了class Info,它有两个成员tag,info。其实就是键值对。
然后定义了AmbiguousInfo,它有一个TTL类型的成员变量,表示如果问你两次你都没回答就不搭理你了。
最后定义了核心类State,这个类包含了从用户查询中提取出来的全部信息。

State类维护了两个Info列表:
keyInfoList
otherInfoList

还定义了一个tag列表:
keyInfoTags

实际上State类就相当于一个字典,里面存放的就是键值对。可以通过对比keyInfoTags和keyInfoList找出缺少的键值。
State就是用来存储解析出来的:分数、省份、文理等信息的。

InfomationRecognition模块:信息识别模块

这个模块在Dict文件夹中定义了几个同义词列表,每一个文件中的内容都是同义词。

  • agree.txt:好、行、恩、可以、没问题
  • asktone.txt:行不行、能不能、是不是、算不算
  • disagree.txt:不、别、否
  • gongneng.txt:什么功能、能做什么、能干什么
  • howabout.txt:怎么样、介绍
  • normalinquire.txt:能上、能报、可以上、可以报

if_agree.py

if_agree.py定义了一个AgreeJudge类,这个类读取agree.txt和disagree.txt中的词语构建同意和不同意两个字典。
判断同意还是不同意时,直接判断query中是否包含“同意字典”中的词语。同意返回1,不同意返回-1,不确定返回0。

    def judge(self, target):# 先判定是否有否定内容,再判断是否有肯定内容for pattern in self.__disagree_pattern:index = target.find(pattern)if index == -1:passelse:return -1for pattern in self.__agree_pattern:index = target.find(pattern)if index == -1:passelse:return 1return 0

major.py:识别用户查询中的专业

major.py用来识别出用户查询语句中的专业信息。
首先定义一个专业字典['历史','语言','中医','中药'......],search()函数定义如下,如果用户查询中包含专业,则返回专业名称。如果不包含,返回None

    def search(self, target):for word in self.__all_major:index = target.find(word)if index == -1:passelse:return wordreturn None

target.py:识别出用户查询中的省份

此文件定义了10个省份列表:

  • 北方、南方
  • 华东、华北、华中、华南
  • 西北、东北、西南
  • 全国

每一个列表都形如['河北','河南','北京'......]
_label_to_list(label)函数将地区名称映射为省份列表。
determin_area、determin_province函数分别用来确定学校是否属于某个地区、学校是否属于某个省份
search_province(query):返回query中包含的省份名称,如果没有返回None

ac_auto.py

此文件实现了一个AC自动机,它读取Dict目录下的normal_inquire、how_about、ask_tone、gongneng四个词典中的词语构建一棵字典树。

infomation_extraction:信息抽取模块

这一部分代码是我最看不懂的代码,也是离自然语言处理最近的代码。

本模块用到了

  • 哈工大分词器LTP,命名实体识别
  • jieba分词器
  • ahocorasick,即AC自动机,python中有AC自动机的包

在_types.py文件中定义了需要提取到的信息的枚举

from enum import Enumclass Attribute(Enum):ORIGIN = 0TYPE = 1SCORE = 2DESTINATION = 3SCHOOL = 4

根包下的文件

process.py是最重要的文件,它集中调用上面各个模块。
XQGKFlask是微信接口,本系统调用了wechatpy包。


http://www.ppmy.cn/news/657721.html

相关文章

小强的游戏

题目描述 我国某位著名的信息学专家曾说过一句名言&#xff1a;信息学是聪明人的游戏。 小强在汇英培训中心开展信息学培训&#xff0c;就是想让更多孩子成为聪明人。今天是培训的第一节课&#xff0c;小强让同学们一起做一个游戏&#xff1a; 每个同学从1开始写起&#xff0c…

小说阅读器未能连接服务器怎么办,小强小说阅读器无法加载小说章节的解决方法...

小强小说阅读器是一款十分热门的小说阅读工具&#xff0c;其免费为用户提供了海量的小说资源&#xff0c;而且其根据分类、热门等方式进行排序&#xff0c;方便您查找到自己喜欢的小说&#xff0c;随着各大小说软件、网站的收费&#xff0c;小强小说阅读器由于具备免费、界面清…

JAVA -- sm3加密签名,以及防止重复攻击

背景&#xff1a; 后端开发基本都遇到过使用签名校验的情况&#xff0c;签名的作用是为了防止请求数据被别人截取篡改重新请求。 为什么签名验证可以防止请求数据被篡改&#xff0c;因为一般签名的规则就是&#xff0c;你的所有请求参数&#xff0c;按照约定好的格式进行拼接&a…

【每日一短语】给某人严重的惊吓

1、短语及释义 scare the pants off sb. 释义&#xff1a; 把某人的裤子吓掉&#xff1b;引申为严重的惊吓 2、示例及出处 美剧&#xff1a;《生活大爆炸》第八季第2集 The Big Bang Theory, Season 8 Episode 2 Leonard Hofstadter: I think the idea that someone could be …

4.22 虾皮_小米_度小满

虾皮 面试官问了很多spark 细节的问题。job划分&#xff0c;热点数据&#xff0c;小文件处理方式&#xff0c;shuffle&#xff0c;数据倾斜&#xff0c;orc文件的优势。 现在想起来&#xff0c;虽然答了&#xff0c;但是答得不好。 sql 没写出来。 分段平均和分段 top值 算…

小米6无人直播详细教程+工具包

最新2021版小米6刷无人直播更新包 链接&#xff1a;https://pan.baidu.com/s/1QTqJnAQpOb4HAsD28PkrKA 提取码&#xff1a;2021 百度网盘下载到电脑解压有教程工具

小米MoGA

MoGA 是个分类网络&#xff1a; https://github.com/xiaomi-automl/MoGA/blob/master/models/MoGA_C.py

UA287Q蓝牙模组,UA800 Wi-Fi模组助力扫地机器人方案,为传统电器插上“智能”翅膀

一屋不扫&#xff0c;何以扫天下? 随着人们生活水平的日益提升&#xff0c;大众对智能化的追求也越来越高&#xff0c;扫地机器人这样的智能家居产品便应运而生&#xff0c;它的出现&#xff0c;为我们带来更加便利、舒适的家居体验。接下来&#xff0c;我们就一起来看下&…