项目名称:基于课程标签分类的智能推荐专家系统v1.0
软件环境:Windows 10 64位 家庭中文版
硬件环境:
开发工具:Pycharm 21.2 ,Qt Designer
开发语言:Python 3.8
项目描述:上周的人工智能实验课要求我们做一个基于Python的简单的专家系统并用PyQt5实现图形化界面。于是我结合之前爬取的bilibili和慕课网的网课信息做了一个基于课程标签分类的智能推荐专家系统
参考文章:基于Python/PYQT5的动物识别专家系统(人工智能实验)
推理:
开始界面:
添加规则:
开始推理:
# -*- coding: utf-8 -*-
from itertools import combinations
import xlrd
import openpyxl
class CourseRecommendExpertSystem:
def __init__(self,process):
self.process = process
#初始化规则库
self.rule_base = []
# 读取excel表,将前提和结论存入三维列表
def read(self):
#清空rule_base
self.rule_base.clear()
#读取规则库
data = xlrd.open_workbook("rule_base.xlsx")
table = data.sheets()[0]
row = table.nrows
col = table.ncols
for i in range(1, row):
rule = []
conclusion = []
premise = []
for j in range(col):
title = table.cell_value(0, j)
if title == '结论':
conclusion.append(table.cell_value(i, j))
elif title == '前提':
if table.cell_value(i, j) != '':
premise.append(table.cell_value(i, j))
rule.append(conclusion)
rule.append(premise)
self.rule_base.append(rule)
# print(self.rule_base)
#将用户输入的规则写入规则库
def write(self,rules):
#print(rules)
#读取现有规则库的行数
wb = openpyxl.load_workbook(r'rule_base.xlsx')
ws = wb['Sheet1']
row = ws.max_row
#print(ws.max_row)
# 取出distance_list列表中的每一个元素,openpyxl的行列号是从1开始取得
for i in range(1, len(rules) + 1):
rule = rules[i - 1]
# 写入位置的行列号可以任意改变,这里我是从第2行开始按行依次插入第11列
ws.cell(row=row+1, column=i).value = rule
# 保存操作
wb.save(r'rule_base.xlsx')
#print("规则添加成功!")
#求解不可重复组合数
def combination(self,num_list):
'''''
生成组合,不限元素个数
列表中元素不允许重复出现
组合数计算为:2^n,其中n为num_list列表中元素个数
'''
res_list = []
for i in range(len(num_list) + 1):
res_list += list(combinations(num_list, i))
del(res_list[0])#去掉空集
#print(res_list)
return res_list
#查找结论
def find_conclusion(self,facts):
#初始化返回列表
result = []
#print("已知事实:{}".format(facts))
#遍历规则库中的前提列表
for rule in self.rule_base:
#print("已知前提:{}".format(rule[1]))
#判断前提列表中的前提是否都存在于facts中,则返回结论
t = 1
for premise in rule[1]:
if premise not in facts:
t = 0
break
if t:
result.append(rule[0])#将所有结论封装到列表中返回
#print("结论:{}".format(result))
return result
def inference(self,fact_list):
self.process.append("\n开始推理......\n")
#获取规则库
self.read()
#推理:(1)遍历事实列表,根据一个或多个事实(调用itertools库中的combinations方法进行组合)在规则库中查找结论
# (2)如果找到,判断该结论是否已存在结论列表中或者事实列表中,如果为否,则将结论加入事实列表,并存入结论列表,跳转(4)
# (3)如果没找到或者该结论已存在,继续遍历事实列表,回到(2)
# (4)重新遍历事实列表,回到(1)
# (5)事实列表遍历完毕,如果结论列表不为空,则取结论列表中最新的结论输出为结果
#初始化结论列表
conclusion_list = []
self.recu(fact_list,conclusion_list)
#判断推理成功还是失败
if len(conclusion_list) != 0:
self.process.append("\n推理成功!")
self.process.append("结论:{}".format(conclusion_list[-1]))
self.process.moveCursor(self.process.textCursor().End)
#返回课程名称
return conclusion_list[-1]
else:
self.process.append("\n推理失败!")
self.process.moveCursor(self.process.textCursor().End)
#返回False
return False
#递归执行推理过程
def recu(self,fact_list,conclusion_list):
#将事实列表中各元素进行组合
combined_fact_list = self.combination(fact_list)
#遍历组合过的事实列表
for facts in combined_fact_list:
#print(facts)
#和规则库进行匹配,如果前提列表中的前提都存在于facts中,则查找结论成功,否则失败
result = self.find_conclusion(facts)
if len(result) != 0:
#判断该结论是否已存在结论列表
for conclusion in result:
if conclusion[0] not in conclusion_list and conclusion[0] not in fact_list:
#print("查找结论成功!")
self.process.append("{} --> 结论:{}".format(facts, conclusion[0]))
#print("该结论已存储!")
#将结论加入事实列表,并存入结论列表
fact_list.append(conclusion[0])
conclusion_list.append(conclusion[0])
#重新组合事实列表并遍历
self.recu(fact_list, conclusion_list)
else:
#print("{}结论已存在!".format(conclusion[0]))
continue
else:
#print("查找结论失败")
continue
# -*- coding: utf-8 -*-
# 导入程序运行必须模块
import os
import sys
from PyQt5.QtCore import QThread, pyqtSignal
from PyQt5.QtGui import QIcon,QPixmap
from PyQt5.QtWidgets import QApplication, QMainWindow,QGraphicsPixmapItem, QGraphicsScene
from main_window import Ui_MainWindow
from CourseRecommendExpertSystem import CourseRecommendExpertSystem
import xlrd
import requests
headers={
'User-Agent':'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.25 Safari/537.36 Core/1.70.3877.400 QQBrowser/10.8.4506.400'
}
#规则库线程类
class RulesThread(QThread):
_signal = pyqtSignal(str)
def __init__(self,courseName,graphicsView,label_course_name):
self.courseName = courseName
self.graphicsView = graphicsView
self.label_course_name = label_course_name
super(RulesThread,self).__init__()
def run(self):
course_list = self.getItem()
#print(course_list)
for course in course_list:
if course['courseName'] == self.courseName:
#获取课程main_graph_url
main_graph_url = course['mainGraph']
#对url发起请求获得图片
try:
# 创建image文件夹
if not os.path.exists('./image'):
os.mkdir('./image')
picture_path = './image/' + self.courseName + '.jpg'
#如果图片不存在,则请求图片资源
if not os.path.exists(picture_path):
picture = requests.get(url=main_graph_url,headers=headers)
# 保存
with open(picture_path, 'wb') as f:
f.write(picture.content)
#Graphics View 控件显示图像
self.graphicsView.scene_img = QGraphicsScene()
self.imgShow = QPixmap()
self.imgShow.load(picture_path)
self.imgShowItem = QGraphicsPixmapItem()
self.imgShowItem.setPixmap(QPixmap(self.imgShow))
#self.imgShowItem.setPixmap(QPixmap(self.imgShow).scaled(131, 91)) #自己设定尺寸
self.graphicsView.scene_img.addItem(self.imgShowItem)
self.graphicsView.setScene(self.graphicsView.scene_img)
self.graphicsView.fitInView(QGraphicsPixmapItem(QPixmap(self.imgShow))) #图像自适应大小
#self.label_course_name.setText(self.courseName)
#发生送课程链接给主进程
text = " + course['url'] + "\">" +course['courseName']
self._signal.emit(text)
#print(picture)
except Exception as e:
print(e)
print("请求图片数据失败!")
break
#读取excel表,将每一行封装成字典,存入列表
def getItem(self):
data = xlrd.open_workbook("course_info.xlsx")
table = data.sheets()[0]
row = table.nrows
col = table.ncols
List = []
for i in range(1, row):
dict = {
}
for j in range(col):
title = table.cell_value(0, j)
value = table.cell_value(i, j)
dict[title] = value
List.append(dict)
return List
#主窗口
class MyMainForm(QMainWindow, Ui_MainWindow):
def __init__(self, parent=None):
super(MyMainForm, self).__init__(parent)
self.setupUi(self)
self.conclusion.setPlaceholderText("请输入结论")
self.premise1.setPlaceholderText("前提1")
self.premise2.setPlaceholderText("前提2")
self.premise3.setPlaceholderText("前提3")
self.premise4.setPlaceholderText("前提4")
self.premise5.setPlaceholderText("前提5")
self.fact1.setPlaceholderText("事实1")
self.fact2.setPlaceholderText("事实2")
self.fact3.setPlaceholderText("事实3")
self.fact4.setPlaceholderText("事实4")
self.fact5.setPlaceholderText("事实5")
# 调用CourseRecommendExpertSystem类中的read方法读取规则库
self.CRESys = CourseRecommendExpertSystem(self.process)
self.CRESys.read()
rule_base = self.CRESys.rule_base
#print(rule_base)
for rule in rule_base:
str_rule = '结论:'+rule[0][0]+' 前提:'
for premise in rule[1]:
str_rule += premise + ' '
self.rules.append(str_rule+'\n')
#开始推理按钮绑定信号
self.startButton.clicked.connect(self.inference)
#添加规则按钮绑定信号
self.addButton.clicked.connect(self.add_rule)
def inference(self):
# 获取事实
fact_list = []
t = 0
if self.fact1.text():
fact_list.append(self.fact1.text())
t = 1
if self.fact2.text():
fact_list.append(self.fact2.text())
t = 1
if self.fact3.text():
fact_list.append(self.fact3.text())
t = 1
if self.fact4.text():
fact_list.append(self.fact4.text())
t = 1
if self.fact5.text():
fact_list.append(self.fact5.text())
t = 1
if not t:
self.process.append("请输入至少一个事实!")
self.process.moveCursor(self.process.textCursor().End)
return
#print(fact_list)
courseName = self.CRESys.inference(fact_list)
if courseName:
#开启线程,获取课程相关信息
#必须加self ,不然会闪退
try:
self.rules_thread = RulesThread(courseName,self.graphicsView,self.label_course_name)
#绑定信号返回函数
self.rules_thread._signal.connect(self.set_text) # 进程连接回传到GUI的事件
self.rules_thread.start()
except:
print("线程出错!")
def set_text(self,text):
self.label_course_name.setText(text)
self.label_course_name.setOpenExternalLinks(True)
def add_rule(self):
#获取规则
rules = []
t = 0
if self.conclusion.text():
rules.append(self.conclusion.text())
else:
self.process.append("结论不能为空!")
self.process.moveCursor(self.process.textCursor().End)
return
if self.premise1.text():
t = 1
rules.append(self.premise1.text())
if self.premise2.text():
t = 1
rules.append(self.premise2.text())
if self.premise3.text():
t = 1
rules.append(self.premise3.text())
if self.premise4.text():
t = 1
rules.append(self.premise4.text())
if self.premise5.text():
t = 1
rules.append(self.premise5.text())
if not t:
self.process.append("请输入至少一个前提!")
self.process.moveCursor(self.process.textCursor().End)
return
#print(rules)
try:
self.CRESys.write(rules)
self.process.append("规则添加成功!")
self.process.moveCursor(self.process.textCursor().End)
str_rule = '结论:' + rules[0] + ' 前提:'
for i in range(len(rules)):
if i == 0:
continue
str_rule += rules[i] + ' '
self.rules.append(str_rule + '\n')
self.rules.moveCursor(self.rules.textCursor().End)
except:
self.process.append("规则添加失败!")
self.process.moveCursor(self.process.textCursor().End)
if __name__ == "__main__":
# 固定的,PyQt5程序都需要QApplication对象。sys.argv是命令行参数列表,确保程序可以双击运行
app = QApplication(sys.argv)
# 初始化
myWin = MyMainForm()
#设置标题图标
myWin.setWindowIcon(QIcon('./f.ico'))
#设置标题
#myWin.setWindowTitle('CET4_CET6真题一键下载(1989-2021)V1.2')
# 将窗口控件显示在屏幕上
myWin.show()
# 程序运行,sys.exit方法确保程序完整退出。
sys.exit(app.exec_())
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'main_window.ui'
#
# Created by: PyQt5 UI code generator 5.15.4
#
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
# run again. Do not edit this file unless you know what you are doing.
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(800, 600)
MainWindow.setMinimumSize(QtCore.QSize(800, 600))
MainWindow.setMaximumSize(QtCore.QSize(800, 600))
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setStyleSheet("background-image: url(:/background/image/background.png);")
self.centralwidget.setObjectName("centralwidget")
self.widget = QtWidgets.QWidget(self.centralwidget)
self.widget.setGeometry(QtCore.QRect(-1, -1, 801, 621))
self.widget.setStyleSheet("background-image: url(:/background/image/background.png);")
self.widget.setObjectName("widget")
self.label = QtWidgets.QLabel(self.widget)
self.label.setGeometry(QtCore.QRect(20, 10, 101, 31))
font = QtGui.QFont()
font.setFamily("微软雅黑")
font.setPointSize(12)
self.label.setFont(font)
self.label.setObjectName("label")
self.label_4 = QtWidgets.QLabel(self.widget)
self.label_4.setGeometry(QtCore.QRect(350, 515, 131, 20))
font = QtGui.QFont()
font.setPointSize(8)
self.label_4.setFont(font)
self.label_4.setObjectName("label_4")
self.label_3 = QtWidgets.QLabel(self.widget)
self.label_3.setGeometry(QtCore.QRect(320, 480, 211, 20))
font = QtGui.QFont()
font.setPointSize(8)
self.label_3.setFont(font)
self.label_3.setStyleSheet("background-color: rgba(255, 255, 255, 255);")
self.label_3.setObjectName("label_3")
self.widget_2 = QtWidgets.QWidget(self.widget)
self.widget_2.setGeometry(QtCore.QRect(330, 505, 43, 43))
self.widget_2.setStyleSheet("background-color: rgba(255, 255, 255, 0);\n"
"background-image: url(:/avator/image/avator2.png);")
self.widget_2.setObjectName("widget_2")
self.label_5 = QtWidgets.QLabel(self.widget)
self.label_5.setGeometry(QtCore.QRect(310, 550, 231, 20))
font = QtGui.QFont()
font.setPointSize(8)
self.label_5.setFont(font)
self.label_5.setObjectName("label_5")
self.process = QtWidgets.QTextBrowser(self.widget)
self.process.setGeometry(QtCore.QRect(20, 280, 421, 121))
font = QtGui.QFont()
font.setPointSize(6)
self.process.setFont(font)
self.process.setStyleSheet("QTextBrowser{border-width:0;border-style:outset}\n"
"background-color: rgba(255, 255, 255, 150);")
self.process.setObjectName("process")
self.label_2 = QtWidgets.QLabel(self.widget)
self.label_2.setGeometry(QtCore.QRect(20, 240, 101, 31))
font = QtGui.QFont()
font.setFamily("微软雅黑")
font.setPointSize(12)
self.label_2.setFont(font)
self.label_2.setObjectName("label_2")
self.label_6 = QtWidgets.QLabel(self.widget)
self.label_6.setGeometry(QtCore.QRect(510, 10, 101, 31))
font = QtGui.QFont()
font.setFamily("微软雅黑")
font.setPointSize(12)
self.label_6.setFont(font)
self.label_6.setObjectName("label_6")
self.label_7 = QtWidgets.QLabel(self.widget)
self.label_7.setGeometry(QtCore.QRect(510, 130, 101, 31))
font = QtGui.QFont()
font.setFamily("微软雅黑")
font.setPointSize(12)
self.label_7.setFont(font)
self.label_7.setObjectName("label_7")
self.graphicsView = QtWidgets.QGraphicsView(self.widget)
self.graphicsView.setGeometry(QtCore.QRect(610, 280, 131, 91))
self.graphicsView.setMinimumSize(QtCore.QSize(131, 91))
self.graphicsView.setMaximumSize(QtCore.QSize(130, 91))
self.graphicsView.setStyleSheet("QGraphicsView{border-width:0;border-style:outset}")
self.graphicsView.setObjectName("graphicsView")
self.label_8 = QtWidgets.QLabel(self.widget)
self.label_8.setGeometry(QtCore.QRect(560, 240, 101, 31))
font = QtGui.QFont()
font.setFamily("微软雅黑")
font.setPointSize(12)
self.label_8.setFont(font)
self.label_8.setObjectName("label_8")
self.conclusion = QtWidgets.QLineEdit(self.widget)
self.conclusion.setGeometry(QtCore.QRect(510, 40, 181, 20))
font = QtGui.QFont()
font.setPointSize(8)
self.conclusion.setFont(font)
self.conclusion.setStyleSheet("QLineEdit{border-width:0;border-style:outset}\n"
"\n"
"")
self.conclusion.setObjectName("conclusion")
self.premise1 = QtWidgets.QLineEdit(self.widget)
self.premise1.setGeometry(QtCore.QRect(510, 70, 51, 20))
font = QtGui.QFont()
font.setPointSize(8)
self.premise1.setFont(font)
self.premise1.setStyleSheet("QLineEdit{border-width:0;border-style:outset}")
self.premise1.setObjectName("premise1")
self.premise2 = QtWidgets.QLineEdit(self.widget)
self.premise2.setGeometry(QtCore.QRect(570, 70, 51, 20))
font = QtGui.QFont()
font.setPointSize(8)
self.premise2.setFont(font)
self.premise2.setStyleSheet("QLineEdit{border-width:0;border-style:outset}")
self.premise2.setObjectName("premise2")
self.premise3 = QtWidgets.QLineEdit(self.widget)
self.premise3.setGeometry(QtCore.QRect(630, 70, 51, 20))
font = QtGui.QFont()
font.setPointSize(8)
self.premise3.setFont(font)
self.premise3.setStyleSheet("QLineEdit{border-width:0;border-style:outset}")
self.premise3.setObjectName("premise3")
self.premise4 = QtWidgets.QLineEdit(self.widget)
self.premise4.setGeometry(QtCore.QRect(690, 70, 51, 20))
font = QtGui.QFont()
font.setPointSize(8)
self.premise4.setFont(font)
self.premise4.setStyleSheet("QLineEdit{border-width:0;border-style:outset}")
self.premise4.setObjectName("premise4")
self.premise5 = QtWidgets.QLineEdit(self.widget)
self.premise5.setGeometry(QtCore.QRect(750, 70, 51, 20))
font = QtGui.QFont()
font.setPointSize(8)
self.premise5.setFont(font)
self.premise5.setStyleSheet("QLineEdit{border-width:0;border-style:outset}")
self.premise5.setObjectName("premise5")
self.addButton = QtWidgets.QPushButton(self.widget)
self.addButton.setGeometry(QtCore.QRect(510, 100, 81, 31))
font = QtGui.QFont()
font.setPointSize(10)
self.addButton.setFont(font)
self.addButton.setStyleSheet("")
self.addButton.setObjectName("addButton")
self.fact1 = QtWidgets.QLineEdit(self.widget)
self.fact1.setGeometry(QtCore.QRect(510, 165, 51, 20))
font = QtGui.QFont()
font.setPointSize(8)
self.fact1.setFont(font)
self.fact1.setStyleSheet("QLineEdit{border-width:0;border-style:outset}")
self.fact1.setObjectName("fact1")
self.fact2 = QtWidgets.QLineEdit(self.widget)
self.fact2.setGeometry(QtCore.QRect(570, 165, 51, 20))
font = QtGui.QFont()
font.setPointSize(8)
self.fact2.setFont(font)
self.fact2.setStyleSheet("QLineEdit{border-width:0;border-style:outset}")
self.fact2.setObjectName("fact2")
self.fact3 = QtWidgets.QLineEdit(self.widget)
self.fact3.setGeometry(QtCore.QRect(630, 165, 51, 20))
font = QtGui.QFont()
font.setPointSize(8)
self.fact3.setFont(font)
self.fact3.setStyleSheet("QLineEdit{border-width:0;border-style:outset}")
self.fact3.setObjectName("fact3")
self.fact4 = QtWidgets.QLineEdit(self.widget)
self.fact4.setGeometry(QtCore.QRect(690, 165, 51, 20))
font = QtGui.QFont()
font.setPointSize(8)
self.fact4.setFont(font)
self.fact4.setStyleSheet("QLineEdit{border-width:0;border-style:outset}")
self.fact4.setObjectName("fact4")
self.fact5 = QtWidgets.QLineEdit(self.widget)
self.fact5.setGeometry(QtCore.QRect(750, 165, 51, 20))
font = QtGui.QFont()
font.setPointSize(8)
self.fact5.setFont(font)
self.fact5.setStyleSheet("QLineEdit{border-width:0;border-style:outset}")
self.fact5.setObjectName("fact5")
self.startButton = QtWidgets.QPushButton(self.widget)
self.startButton.setGeometry(QtCore.QRect(510, 190, 81, 31))
font = QtGui.QFont()
font.setPointSize(10)
self.startButton.setFont(font)
self.startButton.setStyleSheet("")
self.startButton.setObjectName("startButton")
self.textBrowser = QtWidgets.QTextBrowser(self.widget)
self.textBrowser.setGeometry(QtCore.QRect(260, 470, 321, 111))
self.textBrowser.setStyleSheet("QTextBrowser{border-width:0;border-style:outset}\n"
"background-color: rgba(255, 255, 255, 150);")
self.textBrowser.setObjectName("textBrowser")
self.rules = QtWidgets.QTextBrowser(self.widget)
self.rules.setGeometry(QtCore.QRect(20, 40, 471, 192))
self.rules.setStyleSheet("QTextBrowser{border-width:0;border-style:outset}")
self.rules.setObjectName("rules")
self.label_course_name = QtWidgets.QLabel(self.widget)
self.label_course_name.setGeometry(QtCore.QRect(620, 370, 111, 21))
self.label_course_name.setText("")
self.label_course_name.setObjectName("label_course_name")
self.textBrowser.raise_()
self.label.raise_()
self.label_4.raise_()
self.label_3.raise_()
self.widget_2.raise_()
self.label_5.raise_()
self.process.raise_()
self.label_2.raise_()
self.label_6.raise_()
self.label_7.raise_()
self.graphicsView.raise_()
self.label_8.raise_()
self.conclusion.raise_()
self.premise1.raise_()
self.premise2.raise_()
self.premise3.raise_()
self.premise4.raise_()
self.premise5.raise_()
self.addButton.raise_()
self.fact1.raise_()
self.fact2.raise_()
self.fact3.raise_()
self.fact4.raise_()
self.fact5.raise_()
self.startButton.raise_()
self.rules.raise_()
self.label_course_name.raise_()
MainWindow.setCentralWidget(self.centralwidget)
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "基于Python+PyQt5的课程推荐专家系统V1.0"))
self.label.setText(_translate("MainWindow", "规则库:"))
self.label_4.setText(_translate("MainWindow", " 作者: sgsx"))
self.label_3.setText(_translate("MainWindow", "版本 1.1.0 - 2021年11月"))
self.label_5.setText(_translate("MainWindow", "联系方式:[email protected]"))
self.process.setHtml(_translate("MainWindow", "\n"
"\n"
"
"))
self.label_2.setText(_translate("MainWindow", "推理过程:"))
self.label_6.setText(_translate("MainWindow", "添加规则:"))
self.label_7.setText(_translate("MainWindow", "输入事实:"))
self.label_8.setText(_translate("MainWindow", "推荐课程:"))
self.addButton.setText(_translate("MainWindow", "确认添加"))
self.startButton.setText(_translate("MainWindow", "开始推理"))
import picture_rc
我已经将系统完整代码上传Github,有兴趣的读者可以前去下载实现。
https://github.com/sgsx11/Intelligent-recommendation-expert-system-based-on-course-label-classification
该系统仅是初版,还有很多地方需要完善,如果有任何不足,欢迎大家指正!