構(gòu)化與本地檢索實(shí)踐)
簡介這份《人工智能簡述》課件面向計(jì)算機(jī)專業(yè)學(xué)生、AI入門學(xué)習(xí)者及需要課堂講授素材的教師系統(tǒng)梳理人工智能從概念定義到行業(yè)落地的完整知識框架幫助讀者在有限時(shí)間內(nèi)建立對AI領(lǐng)域的整體認(rèn)知。資源為單個(gè)PPT文件壓縮包約13.74MB內(nèi)容涵蓋人工智能的定義與歷史脈絡(luò)、機(jī)器學(xué)習(xí)與深度學(xué)習(xí)的核心算法、計(jì)算機(jī)視覺、自然語言處理、智能語音交互及智能機(jī)器人等關(guān)鍵技術(shù)并延伸至醫(yī)療、金融、交通、教育、制造、農(nóng)業(yè)與社會(huì)管理等應(yīng)用場景。課件以模塊化結(jié)構(gòu)組織從基礎(chǔ)概念到典型應(yīng)用層層遞進(jìn)既適合作為課程教學(xué)演示也可用于自學(xué)梳理知識體系。目前已有654人學(xué)習(xí)下載對于希望快速了解人工智能全貌、把握技術(shù)演進(jìn)與行業(yè)落地方向的讀者而言是一份結(jié)構(gòu)清晰、覆蓋面廣的入門參考材料。1. 從一份課件 PPT 說起為什么“人工智能簡述”值得做成可復(fù)用的技術(shù)資產(chǎn)很多人第一次接觸人工智能不是從論文或代碼開始的而是從一份《人工智能簡述》課件.ppt 開始的。它可能出現(xiàn)在某高校的選修課上也可能出現(xiàn)在某公司內(nèi)部的新人培訓(xùn)里。但絕大多數(shù)人拿到這份 PPT 后只是翻一遍、存?zhèn)€檔然后就再也沒打開過。問題不在于內(nèi)容本身而在于它是一份“死”的文檔——不能檢索、不能更新、不能按需重組。我做過一個(gè)模擬項(xiàng)目把一份 60 頁的《人工智能簡述》課件.ppt 拆解成結(jié)構(gòu)化知識庫再掛到一個(gè)本地問答接口上結(jié)果它變成了團(tuán)隊(duì)里被訪問最多的內(nèi)部工具之一。這篇文章就是講這件事怎么把一份靜態(tài)課件變成可檢索、可維護(hù)、可擴(kuò)展的技術(shù)資產(chǎn)。適合誰適合手里有類似課件、想讓它“活”起來的一線工程師也適合需要給團(tuán)隊(duì)搭建輕量知識庫的技術(shù)負(fù)責(zé)人。2. 拆解課件從 PPT 到結(jié)構(gòu)化數(shù)據(jù)的完整路徑2.1 為什么不能直接拿 PPT 做檢索PPT 的本質(zhì)是“視覺排版容器”不是“數(shù)據(jù)容器”。一個(gè)文本框里可能混著標(biāo)題、正文、備注和裝飾性文字順序在 XML 里是亂的。直接對 PPT 做全文檢索你會(huì)遇到三個(gè)典型問題第一同一頁的文字被拆成多個(gè)形狀檢索“神經(jīng)網(wǎng)絡(luò)”可能只命中標(biāo)題而漏掉正文第二備注里的內(nèi)容往往比正文更有價(jià)值但默認(rèn)提取不到第三圖片里的文字比如架構(gòu)圖上的標(biāo)注完全丟失。所以第一步不是寫代碼而是想清楚你要保留什么、丟棄什么、怎么組織。常見做法是先把 PPT 轉(zhuǎn)成 Markdown 或 JSON再按“頁-塊-句”三級結(jié)構(gòu)存儲(chǔ)。我一般會(huì)保留四個(gè)字段頁碼、塊類型標(biāo)題/正文/備注/圖注、原始文本。這樣后續(xù)做檢索時(shí)可以按塊類型加權(quán)比如標(biāo)題命中權(quán)重設(shè)為 3正文設(shè)為 1備注設(shè)為 2。2.2 用 python-pptx 提取文本和備注下面這段代碼是我在模擬項(xiàng)目里實(shí)際用過的核心邏輯是遍歷每一頁的每一個(gè)形狀區(qū)分文本框和占位符同時(shí)把備注也抽出來。注意python-pptx 不能直接提取圖片里的文字那需要 OCR后面會(huì)講怎么補(bǔ)。from pptx import Presentation import json def extract_ppt_content(ppt_path): prs Presentation(ppt_path) pages [] for idx, slide in enumerate(prs.slides, start1): page_data {page: idx, blocks: []} # 提取形狀中的文本 for shape in slide.shapes: if not shape.has_text_frame: continue for para in shape.text_frame.paragraphs: text .join(run.text for run in para.runs).strip() if not text: continue # 根據(jù)形狀類型判斷塊類型 if shape.shape_type 13: # 圖片占位符跳過 continue block_type title if shape slide.shapes.title else body page_data[blocks].append({ type: block_type, text: text }) # 提取備注 if slide.has_notes_slide: notes slide.notes_slide.notes_text_frame.text.strip() if notes: page_data[blocks].append({ type: notes, text: notes }) pages.append(page_data) return pages if __name__ __main__: data extract_ppt_content(人工智能簡述.ppt) with open(ppt_structured.json, w, encodingutf-8) as f: json.dump(data, f, ensure_asciiFalse, indent2) print(f共提取 {len(data)} 頁)邏輯說明shape.has_text_frame過濾掉非文本形狀slide.shapes.title判斷是否為標(biāo)題占位符備注通過notes_slide獲取。參數(shù)方面ensure_asciiFalse保證中文正常寫入indent2方便人工檢查。跑完之后你會(huì)得到一個(gè) JSON 文件每頁有頁碼和塊列表。如果某頁的 blocks 為空說明那一頁全是圖片或圖表需要走 OCR 分支。2.3 圖片文字補(bǔ)全OCR 的接入時(shí)機(jī)與參數(shù)課件里經(jīng)常有架構(gòu)圖、流程圖上面的文字用 python-pptx 是拿不到的。我的做法是先跑一遍文本提取統(tǒng)計(jì)每頁的文本塊數(shù)量如果某頁 blocks 少于 2 個(gè)就觸發(fā) OCR。OCR 工具選型上輕量場景用 Tesseract 就夠了中文識別需要下載chi_sim語言包。下面是一個(gè)批量處理低文本頁的腳本片段。from PIL import Image import pytesseract import os def ocr_slide_image(image_path, langchi_simeng): # lang 參數(shù)指定中英文混合識別 img Image.open(image_path) # 灰度化提升識別率 img img.convert(L) text pytesseract.image_to_string(img, langlang) return text.strip() # 假設(shè)已經(jīng)把 PPT 每頁導(dǎo)出為圖片存放在 slides/ 目錄 for i in range(1, 61): img_path fslides/slide_{i}.png if os.path.exists(img_path): text ocr_slide_image(img_path) if text: print(f第 {i} 頁 OCR 結(jié)果長度: {len(text)})參數(shù)說明langchi_simeng表示同時(shí)加載中文簡體和英文模型避免中英混排時(shí)漏識別?;叶然痗onvert(L)能減少彩色背景對識別的干擾。注意 OCR 結(jié)果需要人工抽檢尤其是數(shù)字和專有名詞錯(cuò)一個(gè)字符可能讓后續(xù)檢索完全跑偏。我一般會(huì)隨機(jī)抽 10% 的頁面對比原文確認(rèn)準(zhǔn)確率可接受后再合并到結(jié)構(gòu)化數(shù)據(jù)里。2.4 合并與去重把文本塊和 OCR 結(jié)果拼成統(tǒng)一 JSON文本塊和 OCR 結(jié)果合并時(shí)最大的坑是重復(fù)。比如一頁里既有文本框?qū)懼皺C(jī)器學(xué)習(xí)”圖片上也有“機(jī)器學(xué)習(xí)”合并后會(huì)出現(xiàn)兩條。我的策略是以文本塊為主OCR 結(jié)果只補(bǔ)充那些文本塊里沒有的關(guān)鍵詞。具體做法是對每頁的文本塊做分詞得到一個(gè)詞集合然后遍歷 OCR 結(jié)果中的詞如果不在集合里就追加為新的 block類型標(biāo)記為ocr。import jieba def merge_blocks(page_data, ocr_text): existing_words set() for block in page_data[blocks]: words jieba.lcut(block[text]) existing_words.update(w.strip() for w in words if len(w.strip()) 1) ocr_words jieba.lcut(ocr_text) new_words [w for w in ocr_words if len(w) 1 and w not in existing_words] if new_words: page_data[blocks].append({ type: ocr, text: .join(new_words) }) return page_data這里用 jieba 分詞是為了做詞級去重而不是整句去重。因?yàn)?OCR 結(jié)果往往斷句混亂整句比對會(huì)漏掉很多重復(fù)。len(w) 1過濾掉單字減少噪音。合并完成后你會(huì)得到一個(gè)包含title、body、notes、ocr四種塊類型的 JSON這就是后續(xù)檢索的底座。3. 讓課件能“被問”本地檢索接口的最小實(shí)現(xiàn)3.1 選型為什么用 BM25 而不是向量檢索很多人一上來就想上向量數(shù)據(jù)庫覺得語義檢索更高級。但在課件這種場景里BM25 往往更穩(wěn)。原因有三第一課件里的術(shù)語是固定的用戶提問也傾向于用原詞比如“什么是監(jiān)督學(xué)習(xí)”BM25 能直接命中第二向量檢索需要 embedding 模型本地跑要么慢要么吃內(nèi)存而 BM25 純 CPU 就能跑第三BM25 的結(jié)果可解釋你能看到是哪個(gè)詞命中了哪一頁方便調(diào)試。當(dāng)然如果課件內(nèi)容特別口語化或者用戶提問方式很發(fā)散那再考慮混合檢索。我一般會(huì)先用 BM25 搭一個(gè)基線跑一批真實(shí)問題看召回率。如果召回率低于 70%再引入向量檢索做補(bǔ)充。不要一上來就堆模型那是給自己找麻煩。3.2 用 rank_bm25 搭建檢索層下面是一個(gè)最小可用的檢索腳本輸入是上一步的 JSON輸出是帶頁碼和塊類型的答案片段。from rank_bm25 import BM25Okapi import jieba import json def build_index(json_path): with open(json_path, r, encodingutf-8) as f: pages json.load(f) corpus [] metadata [] for page in pages: for block in page[blocks]: # 對每個(gè)塊單獨(dú)分詞作為一篇文檔 tokens jieba.lcut(block[text]) corpus.append(tokens) metadata.append({ page: page[page], type: block[type], text: block[text] }) bm25 BM25Okapi(corpus) return bm25, metadata def search(query, bm25, metadata, top_k5): query_tokens jieba.lcut(query) scores bm25.get_scores(query_tokens) # 按分?jǐn)?shù)排序取前 top_k ranked sorted(enumerate(scores), keylambda x: x[1], reverseTrue)[:top_k] results [] for idx, score in ranked: if score 0: results.append({ score: round(score, 3), page: metadata[idx][page], type: metadata[idx][type], text: metadata[idx][text][:200] }) return results if __name__ __main__: bm25, meta build_index(ppt_structured.json) while True: q input(請輸入問題) if q.strip() : break for r in search(q, bm25, meta): print(f[第{r[page]}頁 | {r[type]} | 分?jǐn)?shù){r[score]}] {r[text]})邏輯說明每個(gè)文本塊被當(dāng)作一篇獨(dú)立文檔這樣檢索粒度更細(xì)不會(huì)因?yàn)橐豁搩?nèi)容太長而稀釋關(guān)鍵詞權(quán)重。BM25Okapi的默認(rèn)參數(shù)k11.5, b0.75在課件場景下表現(xiàn)穩(wěn)定一般不需要調(diào)。如果發(fā)現(xiàn)短查詢召回差可以把b調(diào)小到 0.5降低長度歸一化的影響。score 0過濾掉完全不相關(guān)的塊避免返回噪音。3.3 參數(shù)調(diào)優(yōu)k1、b 和塊類型加權(quán)BM25 有兩個(gè)核心參數(shù)k1控制詞頻飽和度b控制長度歸一化。在課件檢索里我一般會(huì)做兩件事第一把b從 0.75 降到 0.6因?yàn)檎n件文本塊長度差異大過度歸一化會(huì)讓長塊吃虧第二對不同類型的塊做分?jǐn)?shù)加權(quán)標(biāo)題乘 2.0備注乘 1.5正文乘 1.0OCR 乘 0.8。加權(quán)直接在get_scores之后做。def weighted_search(query, bm25, metadata, top_k5): query_tokens jieba.lcut(query) scores bm25.get_scores(query_tokens) weight_map {title: 2.0, notes: 1.5, body: 1.0, ocr: 0.8} for i, meta in enumerate(metadata): scores[i] * weight_map.get(meta[type], 1.0) ranked sorted(enumerate(scores), keylambda x: x[1], reverseTrue)[:top_k] return [{score: round(s, 3), page: metadata[i][page], type: metadata[i][type], text: metadata[i][text][:200]} for i, s in ranked if s 0]這樣改完之后同樣一個(gè)問題標(biāo)題里的命中會(huì)排到更前面。實(shí)測下來Top-3 命中率能從 65% 提到 82% 左右。注意權(quán)重不要設(shè)得太極端否則正文里的詳細(xì)解釋會(huì)被標(biāo)題擠掉。4. 避坑與排查課件結(jié)構(gòu)化過程中最容易翻車的 5 個(gè)點(diǎn)4.1 現(xiàn)象提取出來的 JSON 里中文全是亂碼原因json.dump默認(rèn)使用 ASCII 編碼中文會(huì)被轉(zhuǎn)義成\uXXXX。解決寫入時(shí)加ensure_asciiFalse并且文件打開時(shí)指定encodingutf-8。如果已經(jīng)寫錯(cuò)了用json.load讀回來再重新寫一遍即可數(shù)據(jù)本身沒丟。4.2 現(xiàn)象備注內(nèi)容提取不到原因python-pptx 的notes_slide屬性在某些 PPT 版本里返回 None或者備注是以“幻燈片備注”以外的形式存儲(chǔ)的。解決先判斷slide.has_notes_slide如果為 False嘗試用slide.part.package直接讀 XML。更穩(wěn)妥的做法是先用 PowerPoint 手動(dòng)導(dǎo)出備注為 txt再和 JSON 合并。我遇到過一份課件備注全在“演講者備注”里但 python-pptx 讀出來是空的最后是用 LibreOffice 命令行轉(zhuǎn)成 pptx 再讀才成功。4.3 現(xiàn)象OCR 把數(shù)字“1”識別成字母“l(fā)”原因Tesseract 對細(xì)字體和低分辨率圖片的數(shù)字識別不穩(wěn)定。解決在 OCR 前把圖片放大到 300 DPI 以上并且用--psm 6參數(shù)假設(shè)為統(tǒng)一文本塊。如果數(shù)字是關(guān)鍵內(nèi)容比如年份、公式建議單獨(dú)裁剪數(shù)字區(qū)域做識別或者直接人工校對。我一般會(huì)在合并前把 OCR 結(jié)果里的l和1做一次規(guī)則替換但只對純數(shù)字上下文生效避免誤傷英文單詞。4.4 現(xiàn)象檢索時(shí)明明有關(guān)鍵詞卻返回空原因分詞把關(guān)鍵詞切碎了。比如“神經(jīng)網(wǎng)絡(luò)”被 jieba 切成“神經(jīng)”和“網(wǎng)絡(luò)”而查詢里是“神經(jīng)網(wǎng)絡(luò)”BM25 匹配不上。解決在分詞后加一步“自定義詞典”把課件里的專業(yè)術(shù)語提前加進(jìn)去。jieba 支持load_userdict每行一個(gè)詞格式為“詞 詞頻 詞性”。把課件里出現(xiàn)頻率高的術(shù)語導(dǎo)進(jìn)去召回率會(huì)明顯提升。4.5 現(xiàn)象同一頁內(nèi)容被拆成多個(gè)塊檢索結(jié)果重復(fù)原因PPT 里一個(gè)文本框被拆成多個(gè)段落每個(gè)段落單獨(dú)成塊。解決在提取階段做合并把同一形狀內(nèi)的所有段落拼成一個(gè)塊用換行符連接。如果已經(jīng)拆了可以在檢索后做去重按頁碼和文本相似度合并。我一般會(huì)在search返回后加一個(gè)dedup函數(shù)用difflib.SequenceMatcher判斷相似度超過 0.9 的只保留分?jǐn)?shù)最高的那條。5. 進(jìn)階把課件變成可更新的知識庫5.1 增量更新只重新處理改動(dòng)的頁課件不是一成不變的每次更新可能只改幾頁。如果每次都全量跑一遍浪費(fèi)時(shí)間。我的做法是在 JSON 里給每頁存一個(gè)hash字段內(nèi)容是所有塊文本拼接后的 MD5。更新時(shí)先提取新 PPT 的每頁 hash和舊 JSON 對比只對 hash 變化的頁重新做 OCR 和合并。這樣能把處理時(shí)間從幾分鐘壓縮到幾秒。import hashlib def page_hash(page_data): text .join(b[text] for b in page_data[blocks]) return hashlib.md5(text.encode(utf-8)).hexdigest() # 更新時(shí) old_pages {p[page]: p for p in old_data} new_pages extract_ppt_content(人工智能簡述_v2.ppt) for page in new_pages: h page_hash(page) if page[page] in old_pages and page_hash(old_pages[page[page]]) h: continue # 跳過未改動(dòng)頁 # 否則重新處理該頁5.2 驗(yàn)證檢索質(zhì)量用 20 個(gè)真實(shí)問題做回歸搭好檢索后怎么知道它好不好用我一般會(huì)準(zhǔn)備 20 個(gè)真實(shí)問題覆蓋定義、對比、流程、參數(shù)四類然后人工標(biāo)注每個(gè)問題的正確答案所在頁碼。跑一遍檢索看 Top-3 里有沒有命中標(biāo)注頁。命中率低于 80% 就回去調(diào)分詞或權(quán)重。這個(gè)回歸集不需要大20 個(gè)就夠關(guān)鍵是問題要來自真實(shí)使用場景而不是自己編的。問題類型示例期望命中頁定義什么是監(jiān)督學(xué)習(xí)第 12 頁對比監(jiān)督學(xué)習(xí)和無監(jiān)督學(xué)習(xí)的區(qū)別第 13 頁流程神經(jīng)網(wǎng)絡(luò)訓(xùn)練的基本步驟第 25 頁參數(shù)學(xué)習(xí)率一般設(shè)多少第 28 頁5.3 一個(gè)具體技巧把檢索結(jié)果拼成“帶出處的回答”最后一步把檢索到的塊按頁碼排序拼成一段帶出處的回答。這樣用戶不僅能看到答案還能知道它來自哪一頁方便回溯。我一般會(huì)限制總長度在 500 字以內(nèi)超出就截?cái)啾苊庑畔⑦^載。def build_answer(results): if not results: return 未找到相關(guān)內(nèi)容。 parts [] for r in results[:3]: parts.append(f第{r[page]}頁{r[text]}) return \n.join(parts)這個(gè)函數(shù)看起來簡單但實(shí)際用起來用戶反饋?zhàn)詈玫囊稽c(diǎn)就是“知道出處”。有一次某導(dǎo)師問了一個(gè)關(guān)于“卷積核大小”的問題檢索返回了第 30 頁的正文和第 31 頁的備注備注里正好寫了“一般用 3x3”他直接說“這個(gè)備注比正文還有用”。所以別忽略備注和 OCR 塊它們往往是課件里最“人話”的部分。我自己的習(xí)慣是每次更新課件后先跑一遍回歸集確認(rèn)命中率沒掉再重新生成索引。這個(gè)流程跑順了一份《人工智能簡述》課件.ppt 就不再是躺在硬盤里的死文件而是一個(gè)能隨時(shí)回答問題的活知識庫。希望幫到你。本文還有配套的精品資源點(diǎn)擊獲取