更新时间:2023-5-1 14:00
1 题目
某 MOOC 在线教育平台希望能够进行个性化教学,实现用户自主学习。在用户学习时,系统从题库中随机抽取若干道与例题同步的随堂测试题,记录、分析学生的学习和答题信息,并且课后会自动生成作业题(或练习题)。此外,系统还能够定期回溯学生的易错题所涉及的内容,自动推荐题型相似、难度有层次的其他题目供用户进行拓展练习。为实现这样的功能,如何度量题目之间的相似性,如何评估题目的难度,是该产品要解决的关键问题。以小学数学应用题 1 为例,度量题目之间相似性的依据主要有以下两种:
- 题干文字。这种方法一般只能找到与题干文字相近的题目作为相似题目。但是,有些题目的题干文字相似,而关键字词不同,题意差异较大;有些问题的背景可能毫无关联,题干文字也几乎不同,而题目的解题思路与方法技巧却完全相同。因此,这种方法效果有限。
- 事先标注题目的知识点等信息。这种方法的推荐效果取决于知识点的划分方式和粒度。知识点划分太粗,推荐结果可能与例题或用户的易错题差别太大;知识点划分太细, 推荐结果可能太单一。两种情况下都无法真正达到拓展练习的目的。
评估题目难度的常见做法主要有以下两种:
-
根据考试的类型确定。比如,数学竞赛的试题一般比某个小学期末考试题目难。
-
教师根据经验主观判断。
上述判断题目相似性和评估难度的做法都有明显的局限性。该公司聘请你们团队尝试解决这些问题。以小学数学应用题为例,具体任务如下:
-
设计刻画两道小学数学应用题之间相似性的度量方法。
-
建立评估小学数学应用题难度的数学模型。
-
附件1是一个示例题库,包含 100 道应用题。请将附件1中的题目,按相似性或难度分类(不限制某一道题目只能属于一个分类)。如果某道题目没有相似题目,可以单独成一类。评估算法的复杂度,能否适用于更大规模的题库。
-
附件2中包含10道题目,请使用上述模型或方法分析这些题目的难度,并对于其中的 每一道题目,在附件 1 中找出最相似的一道或若干道题目(没有相似题目可写“无)”。评估算法的复杂度,能否适用于更大规模的题库。
注 1:题目所述小学数学应用题,是指以四则运算为主要求解方法,有一定实际背景的问题。
注 2:教学中还有一种确定题目难度的常见做法,即根据题目的实际得分率来定义题目的难度。但是,题目的实际得分率不仅与学生考前的学习情况有关,还与很多“非技术”因素有关,比如题目所用的词语、句型、语态,甚至是题目在试卷中出现的先后顺序等等;实际的得分率也只能通过采集真实的试卷信息获得,工作量大。因此,本题所关心的是题目的“技术”难度,不考虑实际的得分率。附件说明:
-
附件 1 为 CSV 格式文件,无标题行,共 2 列 100 行。第一列为题目编号,形如“P001”、“P002”等。第二列为题目内容。
-
附件 2 为 CSV 格式文件,无标题行,共 2 列 10 行。第一列为题目编号,形如“Q001”、“Q002”等。第二列为题目内容。
附件2.csv部分内容,
P001 | 将一批糖果分给幼儿园大班小朋友,如果每人分3颗,就余下21颗;如果每人分4颗,就余下6颗。幼儿园大班有小朋友多少人?这批糖果共有几粒? |
---|---|
P002 | 姐妹俩从家出发去上学,姐姐每分钟走50米,妹妹每分钟走45米。如果妹妹比姐姐早走5分钟,那么姐妹俩可同时到达学校。问:家到学校有多远? |
P003 | 钢铁厂用两辆运输车从距工厂90千米的矿山运矿石回来。现有甲、乙两辆运输车,甲车自矿山、乙车自钢铁厂同时出发,相向而行,速度分别为每小时40千米和50千米,到达目的地后立即返回,如此反复进行多次。如果不计装卸时间,且两车不作任何停留,则两车在第三次相遇时,距矿山多少千米? |
附件2.csv部分内容,
Q001 | 一列客车长150米,每秒行30米;一列货车长200米,每秒行20米。两车相向而行。当错车而过时,客车司机多久可以看到货车通过?货车司机多久能看到客车通过? | |
---|---|---|
Q002 | 一批旅客决定分乘几辆大巴车,要使每辆车乘坐同样的人数。起先,每辆车坐22人,发现有一人坐不上车;若是开走一辆空车,那么所有的旅客刚好平均分乘余下的车。已知每辆车的容量不多于32人,问原有多少辆汽车?这些旅客有多少人? |
2 数学模型
2.1 问题一
根据题目描述,我们需要设计一个算法来度量题目之间的相似性,并评估题目的难度。
首先,我们可以将每道题目转化为一个向量形式,该向量包含每个题目的关键信息(例如题目中的数字、关键词等),然后我们可以使用略。请下载完整文档来度量两个向量之间的相似性。具体地,设题目 a a a 和 b b b 分别对应的向量是 a = ( a 1 , a 2 , … , a n ) \boldsymbol{a}=(a_{1},a_{2},\ldots,a_{n}) a=(a1,a2,…,an) 和 b = ( b 1 , b 2 , … , b n ) \boldsymbol{b}=(b_{1},b_{2},\ldots,b_{n}) b=(b1,b2,…,bn),则两个题目的相似性可以表示为:
。。。。略,请下载完整文档
其中 a ⋅ b \boldsymbol{a}\cdot\boldsymbol{b} a⋅b 表示向量 a \boldsymbol{a} a 和 b \boldsymbol{b} b 的内积, ∥ a ∥ \|\boldsymbol{a}\| ∥a∥ 和 ∥ b ∥ \|\boldsymbol{b}\| ∥b∥ 分别表示向量 a \boldsymbol{a} a 和 b \boldsymbol{b} b 的范数。
但是,直接使用题目中的所有关键信息作为向量可能会导致相似度计算的误差。因此,我们需要对题目的关键信息进行筛选和加权,以提高相似性度量的准确性。具体实现步骤如下
。。。略
2.2 问题二
将题目将采用KMeans聚类算法分为三类,困难、中等、简单。先聚类,再对每个类别进行数据分析,分析出哪个类别具体属于哪个难度。
TF-IDF模型用于将每个题目描述表示为一个向量,向量中的每个元素表示该单词在该题目描述中的权重,以此来表示不同描述的相似度。K-Means聚类模型用于将题目描述向量进行聚类,将相似的题目归为同一类别。聚类模型的数量可以设置为需要进行分类的类别数目。每个题目将会被分为不同的类别。
因此,可以将以上Python代码用数学模型表示为:
设题目集合为
Q
Q
Q,其中题目数目为
N
N
N。
TF-IDF模型:
定义词频矩阵
X
∈
R
N
×
M
\mathbf{X} \in \mathbb{R}^{N \times M}
X∈RN×M,其中第
i
i
i 行第
j
j
j 列的元素
x
i
,
j
x_{i,j}
xi,j 表示题目
i
i
i 中单词
j
j
j 的词频。
定义倒排文档频率(IDF)向量
i
d
f
∈
R
M
\mathbf{idf} \in \mathbb{R}^{M}
idf∈RM,其中第
j
j
j 个元素
i
d
f
j
=
log
N
d
f
j
idf_j = \log \frac{N}{df_j}
idfj=logdfjN,其中
d
f
j
df_j
dfj 表示单词
j
j
j 在总题目数中出现的次数。
定义TF-IDF矩阵
W
∈
R
N
×
M
\mathbf{W} \in \mathbb{R}^{N \times M}
W∈RN×M,其中第
i
i
i 行第
j
j
j 列的元素
w
i
,
j
w_{i,j}
wi,j 表示通过TF-IDF模型计算的题目
i
i
i 中单词
j
j
j 的权重,即
w
i
,
j
=
x
i
,
j
×
i
d
f
j
w_{i,j} = x_{i,j} \times idf_j
wi,j=xi,j×idfj。
K-Means聚类模型:
定义聚类结果向量
c
∈
R
N
\mathbf{c} \in \mathbb{R}^{N}
c∈RN,其中第
i
i
i 个元素
c
i
c_i
ci 表示题目
i
i
i 所属的类别编号,范围为
[
1
,
k
]
[1, k]
[1,k],其中
k
k
k 表示聚类的类别数目。
定义聚类簇质心向量
μ
∈
R
k
×
M
\mathbf{\mu} \in \mathbb{R}^{k \times M}
μ∈Rk×M,其中第
i
i
i 行
μ
i
\mu_i
μi 表示聚类中心向量,即属于第
i
i
i 类的题目描述向量的平均值。
定义样本距离度量
d
i
s
t
(
x
i
,
μ
j
)
\mathrm{dist}(x_i,\mu_j)
dist(xi,μj),其中
x
i
x_i
xi 表示第
i
i
i 个题目描述向量,
μ
j
\mu_j
μj 表示第
j
j
j 个聚类中心向量。可以使用欧几里得距离(Euclidean distance)或余弦相似度(Cosine similarity)作为样本距离度量。
定义损失函数
J
(
{
μ
j
}
1
k
,
c
,
W
)
\mathrm{J}(\{\mathbf{\mu}_j\}_1^k,\mathbf{c},\mathbf{W})
J({μj}1k,c,W),用于衡量聚类的准确性,可以使用误差平方和(SSE)或其它聚类指标。
聚类模型的目标是最小化损失函数
J
\mathrm{J}
J,并得到最优的聚类结果
c
\mathbf{c}
c 和聚类簇质心向量
μ
\mathbf{\mu}
μ。
Kmeans聚类效果的评价指标一般有轮廓系数(Silhouette Coefficient)、Calinski-Harabasz Index和Davies-Bouldin Index等。其中轮廓系数是最常用的评价指标,计算公式为:
s ( i ) = b ( i ) − a ( i ) m a x { a ( i ) , b ( i ) } s(i)=\frac{b(i)-a(i)}{max\{a(i),b(i)\}} s(i)=max{a(i),b(i)}b(i)−a(i)
其中, a ( i ) a(i) a(i)表示第 i i i个样本与同一簇内其他样本的平均距离, b ( i ) b(i) b(i)表示第 i i i个样本与距离最近的另一簇内所有样本的平均距离。
轮廓系数 s s s的取值范围在 [ − 1 , 1 ] [-1,1] [−1,1]之间,值越大代表聚类效果越好。若某个样本的 s s s值为负数,说明该样本更应该划分到其他簇。
2.3 问题三
采用问题二的聚类算法,或者其他聚类算法。不指定聚类数量的聚类算法有以下几种:
- DBSCAN(Density-Based Spatial Clustering of Applications with Noise)
- OPTICS(Ordering Points To Identify the Clustering Structure)
- HDBSCAN(Hierarchical Density-Based Spatial Clustering of Applications with Noise)
其中,DBSCAN 比较常用,其主要思想是根据局部密度定义簇,具有对噪声数据点不敏感的特点。
注意,对于Kmeans算法,离散点对算法的影响巨大,可以首先对离散点剔除出来,单独做一个类别,或者采用DBSCAN算法。
import pandas as pd
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.cluster import KMeans
from sklearn.metrics import silhouette_score
from pyecharts import Scatter
# 读取csv文件
df = pd.read_csv('附件1.csv',header=None)
# 将题目描述作为特征
tfidf = TfidfVectorizer()
features = tfidf.fit_transform(df[1])
# 聚类模型训练
kmeans = KMeans(n_clusters=3, random_state=0).fit(features)
# 评价聚类效果
score = silhouette_score(features, kmeans.labels_)
print('聚类效果评价指标(Silhouette Score):', score)
# 可视化聚类效果
。。。。略
# 打印聚类结果
for i in range(len(kmeans.labels_)):
print('题目ID:{}, 题目描述:{}'.format(df[0][i], df[1][i]))
print('题目类别:{}'.format(kmeans.labels_[i]))
print('---------------------------')
可以看到聚类效果不是特别理想,还需要进一步改进。
2.4 问题四
用问题一的相似度计算方法。遍历附件1和附件2,依次去计算两个问题的相似度。
import pandas as pd
import jieba
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.metrics.pairwise import cosine_similarity
# 读取csv文件
data = pd.read_csv('附件1.csv')
questions = data['题目']
# 定义一个处理题目的函数,该函数将题目转换为关键词列表,并对不同关键词进行加权,最终返回一个向量:
def process_question(question):
。。。略
return ' '.join(key_words) # 返回空格分隔的关键字字符串
# 将所有题目转换为关键词向量:
vectorizer = TfidfVectorizer()
vectors = vectorizer.fit_transform([process_question(question) for question in questions])
# 最后,计算任意两个题目之间的相似度并输出结果:
for i in range(len(questions)):
for j in range(i+1, len(questions)):
similarity = cosine_similarity(vectors[i], vectors[j])[0][0]
print(f"题目{i+1} 和 题目{j+1} 的相似度为: {similarity:.4f}")
3 完整下载
私信我,或者转到我知乎下载:https://zhuanlan.zhihu.com/p/626139128