2023年数维杯国际大学生数学建模挑战赛
C题 人工智能生成文本的智能识别与检测
原题再现:
近年来,随着信息技术的飞速发展,人工智能的各种应用应运而生。典型应用包括机器人导航、语音识别、图像识别、自然语言处理和智能推荐等。其中,以ChatGPT为代表的大型语言模型(large language models,llm)在世界范围内得到了广泛的推广和应用。同时我们充分认识到这些模式给人们带来的丰富、智能、便捷的体验。同样重要的是要意识到与AI文本生成等工具相关的许多风险。
首先,基于文本对这些大型语言模型进行训练。而不同类型的语言和不同领域的文化背景会对生成的结果产生显著影响。第二,基于数据的人工智能生成结果可能存在语义偏差,缺乏逻辑一致性,缺乏创造性。最后,隐私保护、版权保护以及学生使用人工智能生成论文导致的相关学术不端行为的定义等问题给本科和研究生的教学和培训过程带来了重大困难和挑战。为了防止人工智能生成文本的误用,保证生成内容的质量,探讨如何解决人工智能生成论文所带来的问题,有必要根据主题需求对人工智能生成文本的模式进行识别和检测,包括字段、模型、图像、公式等。
判断文本是否为人工智能生成,除了考虑满足字数要求、生成次数、是否为汉英翻译等因素外。同样重要的是,AI目前缺乏人类的情感和判断力。这会导致语篇生成中出现“多词组少例证、缺乏情感、结构单一等现象或风格”。
请使用数学建模解决以下四个问题:
问题一:请根据附录一提供的20个科学网博客的链接,使用人工智能重写部分文章,寻找人工智能文本生成的基本规则,可以从人工智能生成的字数(如200字、500字等)、生成的次数(第一次生成,然后点击“重新生成”按钮)、是否是中英文翻译、生成文本的风格等方面进行统计推断。
问题二:根据附录二提供的10篇人工智能生成的文章,根据第一个问题得到的模式,判断这些文章生成的次数(不超过5次)、从中文翻译成英文的次数(不超过1次)、从英文翻译成中文的次数(不超过1次)以及输出文章是否有文字要求。
问题三:对于人工智能生成的理论和方法,请仔细考虑文章中的每个段落是否是基于因素人工智能生成的。它包括不同生成语言、是否翻译、生成次数、输出字数是否有约束等,为人工智能生成的理论和方法。然后,标记附录III中提供的十个条款中的每个段落是否由AI生成的结果。
问题四:请建立相关理论和方法,进一步判定文章中的数学模型、图片、公式是否为剽窃内容。并使用附录IV中的示例对此进行演示,并评估所建立的模型。
整体求解过程概述(摘要)
人工智能技术的发展日新月异,由于人工智能潜在的误用,对大型语言模型的广泛关注引起了人们的极大关注,促使人们越来越关注科学有效地检测人工智能生成的文本和文本生成模式。
为了便于进一步的研究,我们量化了文本生成规则并构建了一套描述文本特征的指标。在句法维度上,我们以平均句子长度为指标来衡量写作风格。在语义维度上,分别从词汇和句子两个角度,考虑词汇丰富度和以自我BLEU为度量标准,对文本丰富度进行了测量。
对于问题一,我们利用因子分析模型建立了人工智能生成文本的检测模型。为了获得文本生成规则,我们利用ChatGPT API,根据问题描述制定文本生成模式请求。附录1中的20个文本被分成训练集和测试集,用于随后的模型训练和评估。结果表明,与人工生成的文本相比,人工智能生成的文本词汇丰富度较低,句子间相似度较高,平均句子长度差异不显著。词汇量、生成频率、平均句子长度和词汇量呈正相关。此外,这些因素与自我BLEU呈负相关。在正式语篇和口语语篇中,学术型语篇的平均句子长度较长,词汇丰富度较低,自我BLEU得分较高。
对于问题二,本质上是一个多元分析问题,我们应用因子分析来拟合文本特征和生成规则之间的关系。因子分析作为一种潜在变量建模方法,在多元分析问题中具有数据降维、精度高等优点。
对于问题三,本质上是一个二分类问题,我们应用可分离支持向量机来区分人工智能生成的文本和人工生成的文本。针对训练集样本点不可线性分离的问题,将其映射到高维特征空间,使其线性可分离。在区分人工智能生成的文本之后,我们使用问题2中的方法来确定它们的生成规则。
对于问题4,我们在LaTeX代码中表示公式,并将先前开发的AI文本检测模型应用于剽窃检测。对于图像,我们对其进行分类,提取每个类别的特征,并将其存储在数据库中进行剽窃检测。
问题重述:
考虑到提供的背景和限制条件,我们决定解决以下问题:
问题一:根据词数约束、文本生成次数、翻译条件和语言风格,将人工智能生成的文本划分为不同的模式。计算不同文本模式的统计信息。
问题二:对于附录二提供的段落,我们需要通过每个段落的统计特征来判断每个段落是什么生成模式。
问题三:对于附录三中的段落,我们需要判断它们是否由LLM生成。如果是的话,就像第一题一样,每个段落的生成模式是什么。
问题四:建立模型,分析附录四中的图像、公式、模型是否存在学术不端嫌疑。
模型假设:
假设
假设1:所有段落均由ChatGPT-3.5生成。原因:由于不同的LLM生成不同的文本,不同LLM生成的文本特征不同。为了使检测模型可靠,训练集和测试集的生成模型应该相同。
假设2:附录I中的20个段落是典型的,能够呈现附录II至IV的整体特征。原因:问题询问我们使用附录I作为训练集来判断附录II至IV中的文本是否由LLM生成并推断生成模式。
假设3:本研究充分提取文本信息。原因:文本信息的提取程度对模型的代表性和可靠性至关重要。
假设4:语篇的语言风格大致可分为口语化和正式化两大类。理由:语言的使用通常是由语境决定的,在非正式场合更倾向于简单明了,在正式场合更倾向于遵守正式标准。
模型的建立与求解整体论文缩略图
全部论文及程序请见下方“ 只会建模 QQ名片” 点击QQ名片即可
程序代码:
import sklearn.feature_extraction.text as ft
import jieba
import pandas as pd
import copy
from tqdm import tqdm
import openai
import json
import os
import sklearn.feature_extraction.text as ft
import jieba
import numpy as np
import nltk
import re
from nltk.translate.bleu_score import sentence_bleu
with open("rawtext.txt", "r", encoding='utf-8') as f: #打开文本
rawtext = f.read() #读取文本
#%%整合出人类文本集
text_raw=rawtext.split("||")
for i in range(len(text_raw)):
t=text_raw[i].split("\n")
t=[j for j in t if len(j)>1]
t="||\n".join(t)
text_raw[i]=t
del t
#%%%将篇幅过大的文本拆分成段落,最终得到 92 个样本
for i in range(len(text_raw)):
t=text_raw[i].split("\n")
t=[j.rstrip("||") if len(j)<200 else j for j in t ]
t="\n".join(t)
text_raw[i]=t
del t
text_raw="||".join(text_raw)
text_raw=text_raw.split("||")
text_raw=[i for i in text_raw if len(i)>150 and len(i)<1500]
#%%%存储
print(text_raw)
#%%askgpt
def askgpt(q):
# 目前需要设置代理才可以访问 api
os.environ["HTTP_PROXY"] = "http://10.1.179.67:7890" os.environ["HTTPS_PROXY"] = "http://10.1.179.67:7890" openai.api_key = "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"
rsp = openai.ChatCompletion.create(model="gpt-3.5-turbo", messages=[{
"role": "system", "content": "You are a good assistant" }, {
"role": "user", "content": q
}])
answer = rsp.get("choices")[0]["message"]["content"]
return answer
#%%% 遍历区间
words_constraints = [200, 500]
languages_list = ["中文", "英文"]
styles_list1 = [ "你是一个科普作家,需要让文字轻松活泼,语言风格尽可能口语化。", "你是该领
域的科研专家,在撰写一篇学术论文,需要让语言风格冷静客观"
]
styles_list2 = [ "As a research expert in the field, you are drafting a paragraph for an academic
paper,requiring a language style that is as scholarly, rigorously objective, and compliant with
academic standards as possible.", "You're a science popularizer, aiming for a breezy and lively tone, with language as
casual as possible."
]
regenerate_times = range(0, 5)
answers_list = []
#%%% 中文
for s in tqdm(text_raw):
for words in tqdm(words_constraints):
for style in tqdm(styles_list1):
for i in tqdm(regenerate_times):
q = f"{style}请你用中文重写以下文字,\
形成一段单词数不超过{words}的文字。如果字数不足{words}
词,请你进行扩写到{words}词;\
如果字数超出{words}词,请你进行缩写到{words}词。\
切记,不计标点,空格,你生成的文字单词数尽量接近{words}
词,误差不超过 50 词。 \n {s}" answer = askgpt(q)
answers_list.append(answer)
#%%% 英文
answers_list = []
for s in tqdm(text_raw):
for words in tqdm(words_constraints):
for style in tqdm(styles_list2):
for i in tqdm(regenerate_times):
q = f"{style}Please rewrite the following text in English, creating a
paragraph with a word count equal to {words}. If the word count is below {words} words, \
please expand it to reach {words} words; if it exceeds {words}
words, please condense it to {words} words. \
Remember, punctuation and spaces are not counted,\
and strive to make the word count of your generated text as close to
{words} words as possible, \
with an error margin not exceeding 50 words.\n {s}" answer = askgpt(q)
answers_list.append(answer)
#%% rule
answers=text_raw
rule1=pd.DataFrame(columns=["label","regenerate_times","language","words_constraint","st
yle"])
rule2=pd.DataFrame(columns=["label","regenerate_times","language","words_constraint","st
yle"])
#中文
for words in tqdm(words_constraints):
for j in tqdm(range(2)):#style
for i in tqdm(regenerate_times):
rule1.append(dict(zip(list(rule1.columns)[:-1],[1,i,0,words,j])),ignore_index=True)
#英文
for words in tqdm(words_constraints):
for j in tqdm(range(2)):#style
for i in tqdm(regenerate_times):
rule2.append(dict(zip(list(rule2.columns)[:-1],[1,i,1,words,j])),ignore_index=True)
#%% text-analysis
#%%%将文档集转化为词频矩阵
def Chinese_countarray(X):#X is a list of strings(文档集)
tokenized_text = []
for i in X:
tokenized_text.append(" ".join(jieba.cut(i)))
svac=ft.CountVectorizer()
X=svac.fit_transform(tokenized_text)
vac=svac.get_feature_names_out()
x=X.toarray()
return vac,x
def English_countarray(X):#X is a list of strings(文档集)
tokenized_text = []
svac=ft.CountVectorizer()
X=svac.fit_transform(tokenized_text)
vac=svac.get_feature_names_out()
x=X.toarray()
return vac,x
#%%% 词汇丰富度
def abundant(x):#x 为一个文档集的词频矩阵
xvactotal=x.sum(axis=1)
xvactype=np.count_nonzero(x,axis=1)
y=xvactype/xvactotal
return y
#%%%% test _,x=Chinese_countarray(text_raw)
x_abundant=abundant(x)
#%%% 分句
def Chinese_sent_tokenizer(s):#s is a text(in str)
sentences = re.split('(。|!|\!|\.|?|\?)',s)
new_sents = []
for i in range(int(len(sentences)/2)):
sent = sentences[2*i] + sentences[2*i+1]
new_sents.append(sent)
sentences_number=len(new_sents)
return sentences_number,new_sents
def English_sent_tokenizer(s):#s is a text(in str)
sentences=nltk.sent_tokenize(s)#return a list of sentences
sentences_number=len(sentences)
return sentences_number,sentences
#%%% self-BLEU
def get_bleu_score(sentence, remaining_sentences):
lst = []
for i in remaining_sentences:
bleu = sentence_bleu(sentence, i)
lst.append(bleu)
return lst
def calculate_selfBleu(sentences):
'''sentences - list of sentences generated by NLG system
''' bleu_scores = []
for i in sentences:
sentences_copy = copy.deepcopy(sentences)
remaining_sentences = sentences_copy.remove(i)
print(sentences_copy)
bleu = get_bleu_score(i,sentences_copy)
bleu_scores.append(bleu)
return np.mean(bleu_scores)
#%%%%test
ss,sss=English_sent_tokenizer(s)
ss,sss1=English_sent_tokenizer(s1)
print(calculate_selfBleu(sss)-calculate_selfBleu(sss1))