人工智能入门项目从零构建一个文本相似度比对Web应用想入门人工智能但总觉得那些复杂的模型和算法离自己太远今天我们就来动手做一个看得见、摸得着的AI小项目。不需要你懂高深的数学也不用配置繁琐的环境咱们就用一个下午的时间从零开始搭建一个能判断两段文字有多相似的Web应用。你输入两句话比如“今天天气真好”和“阳光明媚的一天”应用就能立刻告诉你它们的相似度有多高。听起来是不是挺有意思这背后用到的就是一个叫做StructBERT的预训练模型。别被名字吓到你完全不用关心它内部有多复杂我们会用最简单的方式把它“请”过来然后让它为我们工作。整个项目就像搭积木后端用模型计算相似度前端做个简单的网页让用户输入和查看结果。我会手把手带你走通每一步从申请GPU资源、部署模型到写几行前端代码把它们连起来。等你做完不仅收获了一个能跑起来的AI应用更重要的是你能真切地感受到AI是如何解决实际问题的。咱们这就开始吧。1. 项目准备理清思路与备好工具在动手敲代码之前我们先花几分钟把整个项目要做什么、以及需要哪些工具搞清楚。这样后面做起来才不会手忙脚乱。我们这个文本相似度比对应用核心功能很简单用户在网页上输入两段文本点击按钮网页就能显示出一个相似度分数比如0.85代表85%相似。为了实现这个功能我们需要两部分后端服务器端这是大脑。它需要运行一个AI模型StructBERT接收前端发来的两段文本进行计算然后把相似度结果返回。这部分我们会部署在GPU服务器上因为模型计算需要一些算力。前端用户界面这是脸面。一个简单的网页包含两个输入框、一个按钮和一个显示结果的地方。它负责把用户输入的内容发给后端并把后端返回的结果展示出来。对于初学者来说最大的挑战往往不是写代码而是配置AI模型运行的环境。为了解决这个问题我们会使用一个叫“星图”的平台它提供了预置好的AI模型镜像我们可以像安装软件一样一键部署省去了安装各种依赖库的麻烦。你需要准备的东西很简单一个CSDN账号用于登录星图平台。一台能上网的电脑浏览器就行不需要高性能。一颗愿意动手尝试的心这就够了。接下来我们就从最核心的一步开始把那个“大脑”给启动起来。2. 核心引擎部署一键启动StructBERT模型服务模型部署听起来高级但今天我们用的方法极其简单。我们不去手动安装Python、PyTorch、Transformer库这些让人头疼的依赖而是直接使用一个已经全部打包好的“镜像”。你可以把它理解为一个“软件集装箱”里面不仅包含了StructBERT模型本身连它运行所需要的所有系统环境、代码和依赖都预先装好了。我们只需要把这个集装箱放到一台有GPU的服务器上运行起来它就会自动提供一个可以调用的服务接口。2.1 获取并启动模型镜像首先我们需要找到这个“集装箱”。访问镜像市场打开你的浏览器访问星图平台的镜像广场。你可以直接在站内搜索“StructBERT”或“文本相似度”通常能找到已经封装好的模型服务镜像。选择其中一个确保其描述中提供了HTTP API接口。部署实例点击“一键部署”或类似的按钮。平台会引导你进行配置选择GPU规格对于文本相似度计算选择一款基础的GPU例如T4或V100s就完全够用了成本也较低。配置网络与端口确保实例的安全组或防火墙规则开放了一个端口例如7860或8000这是我们的服务对外通信的窗口。启动并获取地址完成配置后启动实例。等待几分钟实例状态变为“运行中”后你会获得一个访问地址通常格式是http://实例IP:端口号。请记下这个地址后文以http://your-server-ip:7860为例这是我们后端服务的门牌号。2.2 验证模型服务服务启动后我们最好先验证一下它是否工作正常。最直接的方法就是用浏览器或命令行工具测试一下它的API。通常这类模型镜像会提供一个简单的测试接口。你可以在浏览器中访问http://your-server-ip:7860或具体的API文档地址看看是否有一个欢迎页面或API说明。更实际的测试是调用其相似度计算接口。我们可以使用curl命令在终端中或者用Python写一个简单的测试脚本。这里用Python示例因为它更直观import requests import json # 替换成你实际的服务地址 server_url http://your-server-ip:7860/api/similarity # 准备要发送的数据 text_pair { text1: 人工智能正在改变世界, text2: AI技术深刻影响着我们的生活 } # 设置请求头告诉服务器我们发送的是JSON格式数据 headers {Content-Type: application/json} try: # 发送POST请求 response requests.post(server_url, datajson.dumps(text_pair), headersheaders) # 打印响应结果 print(状态码:, response.status_code) print(响应内容:, response.json()) except Exception as e: print(请求出错:, e)运行这段代码如果一切正常你应该会收到一个JSON格式的响应里面包含一个similarity_score字段值在0到1之间。看到这个数字恭喜你最复杂的后端部分已经搞定了它正在云端稳稳地运行着随时等待前端的召唤。3. 搭建用户界面创建前端网页后端大脑已经在云端运转起来了现在我们来给它做一个简单的操作面板——也就是前端网页。这个网页只需要三个核心部分两个让用户输入文字的文本框、一个触发计算的按钮以及一个显示结果的地方。我们使用最基础的Web三件套HTML搭建骨架CSS美化一下样子JavaScript负责让页面动起来比如点击按钮后去和后端“说话”。3.1 编写HTML结构创建一个新文件命名为index.html。用任何文本编辑器如VSCode、记事本打开它输入以下代码。这些代码定义了网页的基本结构。!DOCTYPE html html langzh-CN head meta charsetUTF-8 meta nameviewport contentwidthdevice-width, initial-scale1.0 titleAI文本相似度比对工具/title link relstylesheet hrefstyle.css link relstylesheet hrefhttps://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css /head body div classcontainer header h1i classfas fa-robot/i AI文本相似度比对/h1 p classsubtitle输入两段文本让AI模型快速计算它们的语义相似度/p /header main div classinput-area div classtext-box label fortext1i classfas fa-font/i 第一段文本/label textarea idtext1 placeholder请输入第一段文本例如今天天气真不错.../textarea div classchar-count字数: span idcount10/span/div /div div classvs-circle spanVS/span /div div classtext-box label fortext2i classfas fa-font/i 第二段文本/label textarea idtext2 placeholder请输入第二段文本例如阳光明媚的一天.../textarea div classchar-count字数: span idcount20/span/div /div /div div classcontrol-area button idcompareBtn onclickcalculateSimilarity() i classfas fa-bolt/i 开始比对 /button button idclearBtn onclickclearText() classsecondary i classfas fa-broom/i 清空文本 /button /div div classresult-area h2i classfas fa-chart-line/i 相似度结果/h2 div classresult-box !-- 动态显示相似度分数和进度条 -- div classscore-display div classscore-value idsimilarityScore--/div div classscore-label相似度分数/div /div div classprogress-container div classprogress-bar idsimilarityBar/div /div div classscore-text idsimilarityText等待计算.../div /div !-- 显示历史记录 -- div classhistory-section h3i classfas fa-history/i 历史记录/h3 ul idhistoryList !-- 历史记录将通过JavaScript动态添加到这里 -- /ul /div /div /main footer pPowered by StructBERT AI Model | 本工具仅用于演示与学习/p /footer /div script srcscript.js/script /body /html这段代码创建了一个完整的网页结构。它包含了标题、两个文本输入区、控制按钮、结果显示区域甚至还预留了一个显示历史记录的地方。现在这个页面看起来可能有点朴素我们接下来给它加点样式。3.2 添加CSS样式在同一个文件夹下创建一个新文件style.css。这个文件负责让我们的网页变得美观、友好。/* style.css */ * { margin: 0; padding: 0; box-sizing: border-box; font-family: Segoe UI, Tahoma, Geneva, Verdana, sans-serif; } body { background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%); min-height: 100vh; display: flex; justify-content: center; align-items: center; padding: 20px; color: #333; } .container { background-color: white; width: 100%; max-width: 1000px; border-radius: 20px; box-shadow: 0 15px 35px rgba(0, 0, 0, 0.1); overflow: hidden; padding: 30px; } header { text-align: center; margin-bottom: 40px; border-bottom: 2px solid #f0f0f0; padding-bottom: 20px; } header h1 { color: #2c3e50; font-size: 2.8rem; margin-bottom: 10px; } header .subtitle { color: #7f8c8d; font-size: 1.1rem; } /* 输入区域样式 */ .input-area { display: flex; flex-wrap: wrap; justify-content: space-between; align-items: flex-start; gap: 30px; margin-bottom: 40px; } .text-box { flex: 1; min-width: 300px; } .text-box label { display: block; font-weight: 600; margin-bottom: 10px; color: #3498db; font-size: 1.1rem; } textarea { width: 100%; height: 200px; padding: 20px; border: 2px solid #ddd; border-radius: 12px; font-size: 1rem; line-height: 1.6; resize: vertical; transition: all 0.3s ease; } textarea:focus { outline: none; border-color: #3498db; box-shadow: 0 0 0 3px rgba(52, 152, 219, 0.2); } .char-count { text-align: right; font-size: 0.9rem; color: #95a5a6; margin-top: 8px; } /* VS 圆形标志 */ .vs-circle { display: flex; align-items: center; justify-content: center; flex-shrink: 0; } .vs-circle span { background: linear-gradient(135deg, #e74c3c, #e67e22); color: white; width: 70px; height: 70px; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-weight: bold; font-size: 1.5rem; box-shadow: 0 5px 15px rgba(231, 76, 60, 0.3); } /* 控制按钮区域 */ .control-area { display: flex; justify-content: center; gap: 20px; margin-bottom: 40px; } button { padding: 16px 40px; border: none; border-radius: 50px; font-size: 1.1rem; font-weight: 600; cursor: pointer; transition: all 0.3s ease; display: flex; align-items: center; justify-content: center; gap: 10px; } #compareBtn { background: linear-gradient(to right, #3498db, #2ecc71); color: white; box-shadow: 0 4px 15px rgba(52, 152, 219, 0.4); } #compareBtn:hover { transform: translateY(-3px); box-shadow: 0 7px 20px rgba(52, 152, 219, 0.5); } #clearBtn.secondary { background-color: #f8f9fa; color: #6c757d; border: 2px solid #dee2e6; } #clearBtn.secondary:hover { background-color: #e9ecef; border-color: #adb5bd; } /* 结果区域样式 */ .result-area h2 { color: #2c3e50; margin-bottom: 25px; text-align: center; } .result-box { background: #f8f9fa; border-radius: 15px; padding: 30px; text-align: center; margin-bottom: 40px; border: 1px solid #e9ecef; } .score-display { margin-bottom: 25px; } .score-value { font-size: 5rem; font-weight: 800; background: linear-gradient(135deg, #3498db, #9b59b6); -webkit-background-clip: text; background-clip: text; color: transparent; line-height: 1; } .score-label { font-size: 1.2rem; color: #7f8c8d; margin-top: 5px; } .progress-container { height: 20px; background-color: #ecf0f1; border-radius: 10px; margin: 30px auto; overflow: hidden; max-width: 600px; } .progress-bar { height: 100%; width: 0%; background: linear-gradient(to right, #e74c3c, #f39c12, #2ecc71); border-radius: 10px; transition: width 1s ease-in-out; } .score-text { font-size: 1.4rem; font-weight: 600; color: #2c3e50; min-height: 2rem; } /* 历史记录样式 */ .history-section h3 { color: #34495e; margin-bottom: 15px; padding-bottom: 10px; border-bottom: 1px dashed #bdc3c7; } #historyList { list-style-type: none; max-height: 300px; overflow-y: auto; padding-right: 10px; } #historyList li { background: white; margin-bottom: 12px; padding: 18px; border-radius: 10px; border-left: 5px solid #3498db; box-shadow: 0 3px 10px rgba(0,0,0,0.05); display: flex; justify-content: space-between; align-items: center; } .history-text { flex: 1; font-size: 0.95rem; line-height: 1.5; } .history-text span { font-weight: 600; color: #2c3e50; } .history-score { font-weight: bold; font-size: 1.5rem; color: #2ecc71; margin-left: 20px; } footer { text-align: center; margin-top: 40px; padding-top: 20px; border-top: 1px solid #eee; color: #95a5a6; font-size: 0.9rem; } /* 响应式设计 */ media (max-width: 768px) { .input-area { flex-direction: column; } .vs-circle { order: -1; margin: 20px auto; } .control-area { flex-direction: column; align-items: center; } button { width: 100%; max-width: 300px; } .score-value { font-size: 4rem; } }现在双击打开index.html文件你应该能看到一个像模像样的网页界面了。它有了清晰的布局、舒适的色彩和交互元素。不过点击按钮现在还不会发生任何事情因为我们还没把“灵魂”——JavaScript逻辑加进去。4. 注入交互逻辑用JavaScript连接前后端这是让整个应用“活”起来的关键一步。我们需要编写JavaScript代码完成三件事监听用户点击“开始比对”按钮的动作。当按钮被点击时获取两个文本框里的文字打包发送给我们之前部署好的后端API。收到后端返回的相似度分数后用动态的方式在网页上展示出来。在项目文件夹下创建第三个文件script.js然后添加以下代码。// script.js // 1. 定义后端API地址 - 请务必替换成你自己的服务地址 const API_URL http://your-server-ip:7860/api/similarity; // 注意如果浏览器直接访问本地HTML文件去请求另一个域名的接口可能会遇到跨域问题。 // 在实际部署时你需要将前端和后端部署在同一个域名下或者在后端配置CORS允许前端域名访问。 // 为了演示我们这里假设已处理好跨域问题或者你正在使用某些允许跨域的开发环境。 // 2. 页面加载完成后初始化一些功能 document.addEventListener(DOMContentLoaded, function() { // 为两个文本框添加输入事件监听实时显示字数 const textarea1 document.getElementById(text1); const textarea2 document.getElementById(text2); const count1 document.getElementById(count1); const count2 document.getElementById(count2); function updateCharCount(textarea, countElement) { countElement.textContent textarea.value.length; } textarea1.addEventListener(input, () updateCharCount(textarea1, count1)); textarea2.addEventListener(input, () updateCharCount(textarea2, count2)); // 初始更新一次字数 updateCharCount(textarea1, count1); updateCharCount(textarea2, count2); }); // 3. 核心函数计算相似度 async function calculateSimilarity() { const text1 document.getElementById(text1).value.trim(); const text2 document.getElementById(text2).value.trim(); const compareBtn document.getElementById(compareBtn); const scoreElement document.getElementById(similarityScore); const barElement document.getElementById(similarityBar); const textElement document.getElementById(similarityText); // 简单验证输入 if (!text1 || !text2) { alert(请输入两段文本再进行比对); return; } if (text1 text2) { alert(两段文本完全相同相似度应为1。你可以尝试输入一些语义相近但表述不同的句子。); // 即使相同也可以直接显示结果这里我们选择提示后返回 return; } // 禁用按钮防止重复点击并显示加载状态 compareBtn.innerHTML i classfas fa-spinner fa-spin/i 计算中...; compareBtn.disabled true; textElement.textContent AI模型正在分析请稍候...; scoreElement.textContent ...; barElement.style.width 0%; // 准备发送给后端的数据 const requestData { text1: text1, text2: text2 }; try { // 发送POST请求到后端API const response await fetch(API_URL, { method: POST, headers: { Content-Type: application/json, }, body: JSON.stringify(requestData) }); if (!response.ok) { // 如果HTTP状态码不是2xx抛出错误 throw new Error(网络请求失败: ${response.status}); } const result await response.json(); // 假设后端返回的JSON格式为 { similarity_score: 0.85 } // 具体字段名可能需要根据你的实际API响应进行调整 const similarity result.similarity_score || result.score || 0; const displayScore (similarity * 100).toFixed(1); // 转换为百分比并保留一位小数 // 更新页面显示 scoreElement.textContent displayScore %; // 设置进度条宽度动画效果由CSS控制 barElement.style.width ${displayScore}%; // 根据分数显示不同的文本描述 let description ; if (similarity 0.8) { description ✅ 高度相似两段文本表达的意思非常接近。; barElement.style.background linear-gradient(to right, #2ecc71, #27ae60); // 绿色 } else if (similarity 0.5) { description 中度相似。文本在部分主题或观点上有重合。; barElement.style.background linear-gradient(to right, #f39c12, #e67e22); // 橙色 } else { description ⚠️ 相似度较低。两段文本的语义关联性不强。; barElement.style.background linear-gradient(to right, #e74c3c, #c0392b); // 红色 } textElement.textContent description; // 将本次比对加入历史记录 addToHistory(text1, text2, displayScore); } catch (error) { // 处理错误 console.error(计算过程中出错:, error); scoreElement.textContent 出错; textElement.textContent 抱歉计算失败。请检查网络连接及后端服务是否正常。错误信息: ${error.message}; barElement.style.width 0%; barElement.style.background #95a5a6; // 灰色 } finally { // 无论成功失败都恢复按钮状态 compareBtn.innerHTML i classfas fa-bolt/i 开始比对; compareBtn.disabled false; } } // 4. 清空文本函数 function clearText() { document.getElementById(text1).value ; document.getElementById(text2).value ; document.getElementById(count1).textContent 0; document.getElementById(count2).textContent 0; // 同时重置结果展示区域 document.getElementById(similarityScore).textContent --; document.getElementById(similarityBar).style.width 0%; document.getElementById(similarityText).textContent 等待计算...; document.getElementById(similarityBar).style.background linear-gradient(to right, #e74c3c, #f39c12, #2ecc71); // 重置颜色 } // 5. 历史记录功能 function addToHistory(text1, text2, score) { const historyList document.getElementById(historyList); // 创建新的历史记录项 const listItem document.createElement(li); // 截取过长的文本以便显示 const shortText1 text1.length 30 ? text1.substring(0, 30) ... : text1; const shortText2 text2.length 30 ? text2.substring(0, 30) ... : text2; listItem.innerHTML div classhistory-text span文本1:/span ${shortText1}br span文本2:/span ${shortText2} /div div classhistory-score${score}%/div ; // 将新记录插入到列表最前面 historyList.insertBefore(listItem, historyList.firstChild); // 如果历史记录太多移除最旧的一条例如只保留5条 if (historyList.children.length 5) { historyList.removeChild(historyList.lastChild); } }代码关键点说明API_URL这是最重要的变量你必须把它替换成你在第二步获得的真实后端服务地址。fetch API我们使用现代浏览器支持的fetch函数来发送HTTP请求到后端这是一种比传统XMLHttpRequest更简洁的方法。异步 async/awaitcalculateSimilarity函数被标记为async内部使用await等待网络请求返回。这使异步代码写起来像同步代码一样直观。错误处理使用try...catch块来捕获可能的网络错误或API错误并给用户友好的提示。动态更新根据返回的相似度分数我们不仅更新数字还动态改变了进度条的颜色和描述文字让反馈更直观。历史记录每次成功计算后会将简短的文本和分数保存到页面上的一个列表中方便用户对比。现在再次刷新你的index.html页面。在文本框中输入两段话点击“开始比对”。如果一切配置正确你应该能看到进度条滚动最终显示出一个相似度百分比和评价。5. 项目总结与展望走到这一步你已经成功完成了一个完整的人工智能Web应用项目。从在云端一键部署强大的StructBERT模型到亲手编写前端界面再到用JavaScript将前后端流畅地连接起来这个过程涵盖了AI应用落地的几个关键环节。用起来你会发现这个工具虽然界面简单但背后是当前比较先进的自然语言处理模型在支撑。它能理解你输入句子的深层含义而不是简单比较单词是否相同。你可以试试输入“我喜欢猫”和“我对猫咪有好感”或者“这家餐厅价格昂贵”和“此处消费不菲”看看模型给出的分数应该能体会到语义理解的神奇之处。在动手的过程中你可能也遇到或预见到了一些问题比如最典型的跨域请求错误。这是因为浏览器出于安全考虑默认不允许网页向不同域名或端口的服务器发送请求。解决这个问题有两种常见思路一是在部署时将你的前端页面HTML/CSS/JS文件和后端API服务放在同一个域名下二是在后端服务的代码中配置允许前端所在域名进行访问即CORS设置。由于我们使用的是一键部署的镜像修改后端配置可能比较复杂因此第一种“同域部署”是更推荐给初学者的方法。这个项目是一个绝佳的起点。基于这个骨架你可以尝试很多有趣的扩展比如让它可以一次性批量比较多组文本或者加入文本摘要功能先概括再比较甚至尝试接入不同的模型星图镜像市场里有很多比较它们的效果差异。前端也可以做得更美观或者打包成一个浏览器插件。最重要的是通过这个小小的项目你亲手打通了从AI模型到用户产品的路径。这比任何理论都更能让你理解人工智能技术是如何一步步变成我们手中可用的工具的。希望这个体验能激发你继续探索的兴趣去构建更复杂、更有创意的AI应用。获取更多AI镜像想探索更多AI镜像和应用场景访问 CSDN星图镜像广场提供丰富的预置镜像覆盖大模型推理、图像生成、视频生成、模型微调等多个领域支持一键部署。