首页 / 资讯中心 / 文章详情

Django与NLP实战:文本相似度计算系统源码解析

Django与NLP实战:文本相似度计算系统源码解析 ★ FEATURED ARTICLE
简介本资源是一套面向高校计算机相关专业毕业设计场景的文本相似度计算系统完整资料适合正在准备NLP方向毕设、课程设计或需要Python文本处理实战参考的学生与开发者。压缩包内共1个docx文件约749KB内容为系统源码与论文文档的整合稿涵盖绪论、可行性分析、技术选型与系统实现等章节可据此梳理从文本清洗、分词、TF-IDF关键词提取到余弦相似度计算与可视化展示的完整链路。文档同时给出Python、Django、MySQL等技术栈的搭配思路并附摘要、目录与实验评估部分便于读者快速理解系统架构、复用算法模块或作为论文写作与答辩准备的参考。目前已有138人学习下载适合希望以较低成本获取一份结构完整、可直接借鉴的文本相似度项目方案的学习者。1. 文本相似度计算系统从一份毕业设计源码看 Django 与 NLP 的落地组合如果你手头正好有一份基于 Python 的文本相似度计算系统源码打开后发现它不只是一个跑在终端里的算法脚本而是带登录界面、新闻管理、文件分析、可视化结果展示的完整 Web 应用那它大概率就是我今天要拆的这类项目。这类系统在毕业设计里出现频率很高核心目标很明确用户输入两段文本或上传两个 txt 文件系统经过清洗、分词、向量化之后用余弦相似度等算法算出相似度分数再通过前端页面把结果直观地呈现出来。它适合谁一是正在做 NLP 方向课程设计、需要一套可运行参考实现的同学二是想找一个 Django MySQL jieba 的完整项目来练手二次开发的后端新手。整套代码不算庞大但链路完整从表单提交到算法计算再到结果渲染每一层都能看到具体实现拿来改造成新闻去重、问答匹配或者文档查重都算顺手。2. 技术栈与目录结构Django 怎么把算法包成 Web 服务2.1 为什么是 Django MySQL jieba 这套组合这个项目的技术选型没有走冷门路线基本是 Python Web 开发里最稳的那一档。后端用 Django原因很直接它自带 ORM、表单组件、模板引擎和用户认证对于文本相似度这种需要快速搭出输入页、结果页和后台管理页的场景能省掉大量重复代码。数据库选 MySQL是因为系统里要存用户信息、新闻文本、操作记录和关键词词向量表关系型数据库在结构化存储和查询上更顺手。文本处理这块中文分词用 jieba英文分词和停用词过滤用 nltk向量化用词袋模型配合 TF-IDF 加权相似度计算默认走余弦相似度同时保留欧几里得距离、曼哈顿距离和 Jaccard 相似度作为可切换选项。我一般会先确认项目根目录下有没有 requirements.txt里面通常会列 Django、jieba、nltk、numpy、mysqlclient 这几个包。如果没有按下面这个清单手动装也能跑起来pip install django3.2 pip install jieba pip install nltk pip install numpy pip install mysqlclient提示mysqlclient 在 Windows 上编译容易报错常见做法是换成 pymysql然后在项目init.py 里加两行import pymysql和pymysql.install_as_MySQLdb()。2.2 目录结构长什么样每个文件夹负责什么这类项目的目录结构通常比较规整和标准 Django 工程差别不大但会多出算法相关的模块。下面是我根据源码正文和常见实现整理出来的典型结构text_similarity_system/ ├── manage.py ├── requirements.txt ├── text_similarity/ # 项目配置目录 │ ├── settings.py # 数据库、静态文件、中间件配置 │ ├── urls.py # 总路由 │ └── wsgi.py ├── apps/ │ ├── users/ # 用户管理、登录注册 │ ├── news/ # 新闻管理、新闻列表与详情 │ ├── analysis/ # 文本分析、文件分析核心业务 │ │ ├── views.py # 接收表单、调用算法、返回结果 │ │ ├── forms.py # TextSimilarityForm 等表单类 │ │ ├── models.py # 新闻、操作记录、词向量表模型 │ │ └── utils/ │ │ ├── preprocess.py # 清洗、分词、去停用词 │ │ ├── feature.py # 词袋模型、TF-IDF 加权 │ │ └── similarity.py # 余弦、欧几里得、Jaccard 计算 │ └── dashboard/ # 首页统计、个人信息 ├── templates/ # 前端模板基于 Bootstrap ├── static/ # CSS、JS、图片 └── media/ # 上传的 txt 文件存放目录这个结构里最值得关注的是apps/analysis/utils/这个目录它把文本处理链路拆成了三个独立文件预处理、特征提取、相似度计算。这样做的好处是算法逻辑和 Web 视图解耦你改算法的时候不用动 views.py换前端的时候也不用碰 utils。对于二次开发来说这种分层比把所有代码塞进一个 views.py 要友好得多。2.3 数据库表设计新闻、操作记录和词向量表怎么存系统里至少涉及三张核心表。新闻表存标题、正文、发布时间和来源操作记录表存每次相似度计算的输入摘要、算法类型、结果分数和操作时间词向量表存关键词及其对应的向量表示用于加速后续匹配。Django 的 ORM 会把它们映射成模型类迁移命令一跑表就自动建好了。# apps/news/models.py from django.db import models class News(models.Model): title models.CharField(max_length200, verbose_name新闻标题) content models.TextField(verbose_name新闻正文) source models.CharField(max_length100, blankTrue, verbose_name来源) created_at models.DateTimeField(auto_now_addTrue) class Meta: db_table news verbose_name 新闻 class OperationLog(models.Model): input_text models.TextField(verbose_name输入文本摘要) algorithm models.CharField(max_length50, verbose_name算法类型) score models.FloatField(verbose_name相似度分数) created_at models.DateTimeField(auto_now_addTrue) class Meta: db_table operation_log这里input_text存的是摘要而不是全文是为了避免操作记录表膨胀过快。algorithm字段记录用户选了哪种算法方便后续做统计对比。score用 FloatField 存浮点数精度够用。如果你要加用户外键就在 OperationLog 里补一个user models.ForeignKey(...)然后在视图里把当前登录用户传进去。2.4 路由与视图的衔接方式Django 的路由分两层项目级 urls.py 负责把不同 app 的路径分发下去app 级 urls.py 负责具体视图函数的映射。文本分析模块通常会有三个入口文本分析页、文件分析页和结果展示页。# apps/analysis/urls.py from django.urls import path from . import views urlpatterns [ path(text/, views.text_analysis, nametext_analysis), path(file/, views.file_analysis, namefile_analysis), path(result/int:log_id/, views.result_detail, nameresult_detail), ]text_analysis处理表单提交和 GET 请求file_analysis接收上传的 txt 文件result_detail根据操作记录 ID 把结果渲染出来。这种设计让结果页可以分享和回看而不是算完就丢。实际开发中我习惯把结果先落库再跳转这样用户刷新页面不会重复计算也方便排查问题。3. 核心算法链路从原始文本到相似度分数的完整实现3.1 文本预处理清洗、分词、去停用词三步走预处理是整个链路里最影响最终效果的一环。原始文本里往往混着标点、数字、特殊符号和大量无意义的高频词如果不处理向量空间会被噪声撑得很大相似度分数也会失真。这个项目的预处理模块做了三件事用正则去掉非文本内容用 jieba 做中文分词用停用词表过滤掉“的”“了”“在”这类词。# apps/analysis/utils/preprocess.py import re import jieba def load_stopwords(pathstopwords.txt): 加载停用词表每行一个词 with open(path, r, encodingutf-8) as f: return set(line.strip() for line in f if line.strip()) STOPWORDS load_stopwords() def clean_text(text): 去除标点、数字和特殊符号只保留中英文 text re.sub(r[^\u4e00-\u9fa5a-zA-Z], , text) return text.strip() def tokenize(text): 中文分词并过滤停用词和单字 text clean_text(text) words jieba.lcut(text) return [w for w in words if w not in STOPWORDS and len(w) 1]clean_text里的正则[^\u4e00-\u9fa5a-zA-Z]表示保留所有中文字符和英文字母其余一律替换成空格。tokenize先分词再过滤len(w) 1是为了去掉单个字因为单字在相似度计算里区分度太低留着反而增加维度。停用词表可以自己维护也可以用现成的中文停用词表放在项目根目录下即可。如果你的文本里英文占比高可以把 jieba 换成 nltk 的 word_tokenize逻辑一样。3.2 特征提取词袋模型加 TF-IDF 加权分词完成后下一步是把词列表变成向量。这个项目用的是词袋模型先收集所有文本中出现过的词组成一个词汇表然后对每段文本统计词汇表中每个词是否出现或出现多少次。但单纯用 0/1 或词频有个问题——“的”“是”这种词在所有文本里都高频出现区分度很低。所以项目里对词向量做了 TF-IDF 加权让稀有词获得更高权重。# apps/analysis/utils/feature.py import math from collections import Counter def build_vocab(token_lists): 根据所有文本的分词结果构建词汇表 vocab set() for tokens in token_lists: vocab.update(tokens) return sorted(vocab) def compute_tf(tokens): 计算词频 counter Counter(tokens) total len(tokens) or 1 return {word: count / total for word, count in counter.items()} def compute_idf(token_lists): 计算逆文档频率 doc_count len(token_lists) word_doc Counter() for tokens in token_lists: for word in set(tokens): word_doc[word] 1 return {word: math.log(doc_count / (freq 1)) for word, freq in word_doc.items()} def vectorize(tokens, vocab, idf): 将单段文本转为 TF-IDF 加权向量 tf compute_tf(tokens) return [tf.get(word, 0) * idf.get(word, 0) for word in vocab]build_vocab把所有文本的词合并去重后排序保证每次运行向量维度一致。compute_idf里的1是平滑项防止某个词在所有文档中都不出现导致除零。vectorize返回的是一个与词汇表等长的浮点数列表这就是后续计算余弦相似度的输入。实际项目中如果文本量很大词汇表可能上万维这时候可以考虑用 sklearn 的 TfidfVectorizer 替代手写实现但手写版的好处是逻辑透明改起来没有黑盒。3.3 相似度计算余弦相似度为主多算法可切换拿到两个 TF-IDF 向量之后余弦相似度就是算它们夹角的余弦值。公式很简单点积除以模长乘积。值越接近 1 表示越相似接近 0 表示越不相关。项目里还实现了欧几里得距离、曼哈顿距离和 Jaccard 相似度用户可以在前端下拉框里切换。# apps/analysis/utils/similarity.py import math def cosine_similarity(vec_a, vec_b): 余弦相似度点积 / (模长乘积) dot sum(a * b for a, b in zip(vec_a, vec_b)) norm_a math.sqrt(sum(a * a for a in vec_a)) norm_b math.sqrt(sum(b * b for b in vec_b)) if norm_a 0 or norm_b 0: return 0.0 return dot / (norm_a * norm_b) def euclidean_distance(vec_a, vec_b): 欧几里得距离越小越相似 return math.sqrt(sum((a - b) ** 2 for a, b in zip(vec_a, vec_b))) def jaccard_similarity(tokens_a, tokens_b): Jaccard 相似度交集 / 并集 set_a, set_b set(tokens_a), set(tokens_b) if not set_a or not set_b: return 0.0 return len(set_a set_b) / len(set_a | set_b)cosine_similarity里对模长为零的情况做了保护避免空文本导致除零异常。euclidean_distance返回的是距离而不是相似度前端展示时需要说明“值越小越相似”。jaccard_similarity直接基于分词后的集合计算不经过向量化适合短文本快速比对。这几种算法各有适用场景余弦适合长文本Jaccard 适合短句欧几里得距离对向量绝对差异敏感。我一般会在结果页把多个算法的分数并排展示让用户自己判断。3.4 视图层怎么串起整条链路算法写完之后需要在 Django 视图里把它们串起来。用户提交表单后视图先取到两段文本分别预处理、合并构建词汇表、计算 IDF、向量化最后调用相似度函数把结果存库并渲染到模板。# apps/analysis/views.py from django.shortcuts import render, redirect from .forms import TextSimilarityForm from .models import OperationLog from .utils.preprocess import tokenize from .utils.feature import build_vocab, compute_idf, vectorize from .utils.similarity import cosine_similarity def text_analysis(request): form TextSimilarityForm() if request.method POST: form TextSimilarityForm(request.POST) if form.is_valid(): text_a form.cleaned_data[text_a] text_b form.cleaned_data[text_b] tokens_a, tokens_b tokenize(text_a), tokenize(text_b) vocab build_vocab([tokens_a, tokens_b]) idf compute_idf([tokens_a, tokens_b]) vec_a vectorize(tokens_a, vocab, idf) vec_b vectorize(tokens_b, vocab, idf) score cosine_similarity(vec_a, vec_b) log OperationLog.objects.create( input_texttext_a[:100], algorithmcosine, scoreround(score, 4) ) return redirect(result_detail, log_idlog.id) return render(request, analysis/text.html, {form: form})这段代码里cleaned_data是 Django 表单验证通过后的干净数据直接拿来用不用担心注入问题。build_vocab和compute_idf都传入两段文本的分词结果保证向量维度对齐。OperationLog.objects.create把输入摘要、算法名和分数落库然后重定向到结果页。这种“POST 后重定向”的模式能防止用户刷新页面时重复提交是 Django 里的标准做法。4. 避坑与常见问题跑不起来时先查这几处4.1 jieba 分词结果为空或全是单字现象是输入一段中文后相似度始终为 0 或极低。原因通常是停用词表把大部分词都过滤掉了或者len(w) 1这个条件太严格而测试文本又恰好以单字词为主。解决方法是先打印tokenize的返回结果确认分词是否正常如果停用词表过大可以适当精简或者把len(w) 1改成len(w) 1再观察效果。4.2 MySQL 连接报错 Access denied 或 Unknown database现象是python manage.py migrate时直接抛异常。原因一般是 settings.py 里的数据库用户名、密码或库名和本地不一致。解决方法是先在 MySQL 里手动建库CREATE DATABASE text_similarity DEFAULT CHARSETutf8mb4;然后核对 settings.py 的 DATABASES 配置确保 USER、PASSWORD、NAME 三项和实际环境一致。如果用的是 pymysql还要确认init.py 里的伪装代码已经加上。4.3 上传 txt 文件后读取乱码现象是文件分析页显示的结果和原文对不上或者直接报 UnicodeDecodeError。原因是 txt 文件可能用了 GBK 编码而代码里默认用 utf-8 读取。解决方法是在打开文件时加编码探测或者统一用open(path, r, encodingutf-8, errorsignore)先忽略无法解码的字符。更稳妥的做法是用 chardet 库检测编码后再读取。4.4 相似度分数偏高不同文本都接近 1现象是随便输入两段不相关的文本余弦相似度也在 0.8 以上。原因通常是词汇表太小或者 IDF 计算时文档数量太少导致权重区分度不够。解决方法是增加对比文本的数量让 IDF 更有意义或者检查预处理是否把太多词过滤掉了导致向量维度极低。另外如果两段文本都很短余弦相似度本身就容易偏高这时候可以改用 Jaccard 相似度做辅助判断。4.5 前端页面样式丢失或按钮无响应现象是页面能打开但 Bootstrap 样式没生效或者点击“开始计算”没反应。原因是 Django 的静态文件配置有问题或者 CSRF token 没正确传递。解决方法是确认 settings.py 里 STATIC_URL 和 STATICFILES_DIRS 配置正确模板里用{% load static %}引入 CSS。表单里必须加{% csrf_token %}否则 POST 请求会被 Django 拦截返回 403。5. 二次开发与改造把这套系统变成你自己的项目拿到这份源码之后最直接的改造方向是换算法。项目里已经预留了余弦、欧几里得、曼哈顿和 Jaccard 四种方法你可以再加一个基于 Word2Vec 的语义相似度用 gensim 训练词向量对每段文本的词向量取平均得到句向量再算余弦。改动点集中在utils/feature.py和utils/similarity.py视图层只需要多传一个算法参数。第二个方向是加批量比对。现在的系统一次只能比两段文本如果你要做新闻去重就需要把新来的新闻和库里所有新闻逐一比对。实现方式是在views.py里加一个循环遍历 News 表把每条新闻的正文和输入文本做相似度计算最后按分数排序返回 Top N。注意这里要做性能优化可以先把所有新闻的向量预先算好存到词向量表里避免每次请求都重新计算。第三个方向是换前端。项目现在用的是 Django 模板加 Bootstrap如果你更习惯前后端分离可以把算法层保留视图层改成 DRF 接口前端用 Vue 或 React 重写。接口设计很简单一个 POST 接口接收两段文本和算法类型返回 JSON 格式的分数和耗时。这样前端可以做成实时计算用户边输入边看分数变化。验证改造是否成功我一般会准备一组基准文本两段完全相同的、两段完全不相关的、两段部分重叠的。每次改完算法都跑一遍这三组看分数是否符合预期。如果相同文本的分数不是 1不相关文本的分数不是接近 0那就说明预处理或向量化环节出了问题。注意改算法时不要动OperationLog的表结构否则历史记录会对不上。如果确实需要加字段用 Django 的 migration 命令生成迁移文件不要手动改数据库。从那以后我每次拿到这类 NLP 项目都强制先跑一遍基准文本验证链路再动任何代码。希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?
咨询建站