Graph of Thoughts(GOT)?
思維圖(Graph of Thoughts)是一種結構化的表示方法,用于描述和組織模型的推理過程。它將信息和思維過程以圖的形式表達,其中節點代表想法或信息,邊代表它們之間的關系。
GOT 的核心思想
-
節點與邊的表示:
- 節點:每個節點表示一個具體的思考或信息點,可以是一個事實、問題或推論。
- 邊:邊表示節點之間的關系,能夠反映出因果關系、邏輯連接等。
-
動態更新:GOT 可以根據新的信息動態更新,允許模型在生成過程中實時調整思路,增強靈活性。
-
推理路徑:通過跟蹤不同的思維路徑,GOT 可以幫助模型探索多種可能的解決方案,而不僅僅是單一路徑。
-
層次性與關聯性:思維圖強調信息的層次性和關聯性,使得復雜問題的解析更加系統化。
相比于COT, TOT?思維圖 Graph of Thoughts(GOT)是一種有助于增強模型推理能力和可解釋性的方法,通過結構化的信息表示,促進多層次和動態的思考,能夠有效應對復雜的自然語言處理任務
個人嘗試GoT 框架:
- 節點生成:通過不同提示生成多個“思想”節點(初步分析、積極分析、消極分析、總結)。
- 邊構建:記錄思想之間的依賴關系(例如從初始思想到分支思想)。
- 評分:用一個簡單的評分函數評估每個思想的質量。
- 改進:選擇得分最高的思想,進一步改進,形成反饋循環。
實際問題
問題:“一個商店的商品原價是100元,打八折后賣出,再加上10元的運費,問最終價格是多少?如果顧客有20元的優惠券,最終支付多少?”
問題特點
- 多步驟計算:
- 原價 100 元 → 打八折(0.8 × 100 = 80 元)。
- 加上運費 10 元(80 + 10 = 90 元)。
- 使用 20 元優惠券(90 - 20 = 70 元)。
- 包含兩個子問題:最終價格(90 元)和最終支付金額(70 元)。
- 條件依賴:
- 每一步計算依賴前一步結果(折扣 → 運費 → 優惠券)。
- 順序性強,但也可能有不同計算路徑(例如先考慮優惠券再加運費)。
- 潛在復雜性:
- 需要理解“八折”(0.8)的含義。
- 可能遺漏條件(例如運費是否打折?)。
- 需要驗證結果的合理性(例如負值檢查)。
- 推理需求:
- 不僅需要計算,還需分解問題、驗證過程、優化表達。
- 適合用 GoT 的圖結構探索多角度推理,而非單一線性過程。
傳統的 Chain-of-Thought (CoT) 會按順序線性推理,而 GoT 的優勢在于:
并行探索:分解問題、計算、驗證可以同時進行。
反饋循環:驗證結果后改進答案。
圖結構:捕捉步驟間的依賴和多樣性。
設計思路
基于問題特點,我設計了一個多層次的 GoT,目標是全面解決這個問題,同時展示 GoT 的復雜性和靈活性。設計分為四個層次,每層有特定目標和節點生成邏輯。
1. 初始層:問題陳述
- 目標:記錄原始問題,作為圖的根節點。
- 設計:
- 直接使用輸入文本作為一個節點。
- 不生成新內容,僅作為后續推理的起點。
- 節點數量:1 個。
- 作用:提供統一的輸入基準。
2. 第一層:分解問題
- 目標:將復雜問題拆分為可管理的子部分。
- 設計:
- 使用三種提示生成分解節點:
- 計算步驟:列出折扣、運費、優惠券的計算順序。
- 關鍵變量:識別 100 元、0.8、10 元、20 元等變量。
- 計算順序:探索可能的計算路徑(例如先優惠再加運費)。
- 每個提示生成一個節點,從初始節點引出。
- 使用三種提示生成分解節點:
- 節點數量:3 個。
- 作用:為后續計算提供不同視角的基礎。
3. 第二層:計算過程
- 目標:基于分解結果執行具體計算。
- 設計:
- 從第一層的每個節點引出三個計算節點:
- 折扣價格:計算 100 × 0.8 = 80 元。
- 加上運費:計算 80 + 10 = 90 元。
- 優惠券后價格:計算 90 - 20 = 70 元。
- 每個第一層節點生成 3 個子節點,總計 9 個。
- 從第一層的每個節點引出三個計算節點:
- 節點數量:9 個。
- 作用:執行核心計算,探索不同分解方式下的結果一致性。
4. 第三層:驗證與優化
- 目標:檢查計算結果的正確性并改進過程。
- 設計:
- 從第二層的每個節點引出三個驗證節點:
- 驗證正確性:檢查計算是否符合問題邏輯(例如 70 元是否合理)。
- 優化步驟:簡化計算過程(例如一步計算 100 × 0.8 + 10 - 20)。
- 檢查遺漏:確認是否忽略條件(例如運費是否打折)。
- 每個第二層節點生成 3 個子節點,總計 27 個。
- 從第二層的每個節點引出三個驗證節點:
- 節點數量:27 個。
- 作用:通過多角度驗證提升答案可靠性。
5. 第四層:匯總最佳結果
- 目標:整合所有分析,輸出最終答案。
- 設計:
- 從所有節點中選擇得分最高的節點。
- 使用提示“綜合所有分析,給出最終答案”生成一個最終節點。
- 從最佳節點引出邊,形成閉環。
- 節點數量:1 個。
- 作用:提煉最優解,回答兩個子問題(90 元和 70 元)。
6 評分機制
- 目標:評估每個思想的質量,選擇最佳路徑。
- 設計:
- 使用關鍵詞匹配(“原價”、“折扣”、“運費”、“優惠券”、“最終價格”)+ 文本長度。
- 關鍵詞越多、內容越詳細,得分越高。
- 作用:模擬人類對答案完整性和準確性的判斷。
7 圖結構特點
- 總節點數:1(初始)+ 3(分解)+ 9(計算)+ 27(驗證)+ 1(匯總)= 41 個。
- 邊數量:3(初始到分解)+ 9(分解到計算)+ 27(計算到驗證)+ 1(驗證到匯總)= 40 條。
- 復雜度:多層分支、反饋循環、非線性探索。
設計細節:
- 模型加載:
- 使用的本地模型 /opt/chenrui/qwq32b/base_model/qwq_32b
- 設置 max_new_tokens=100,確保生成內容足夠詳細。
- 評分函數 score_thought:
- 檢查關鍵詞出現次數,模擬對問題關鍵元素的覆蓋。
-
加權文本長度,鼓勵更詳細的推理。
- 示例:包含“折扣”和“運費”的節點得分高于只提“原價”的節點。
- 生成函數 generate_thought:
- 輸入提示和前一節點文本,生成新思想。
- 使用模型的生成能力模擬推理過程。
- 主函數 complex_graph_of_thoughts:
- 初始節點:直接存儲輸入文本。
- 分解層:循環三種提示,生成 3 個節點,連接到初始節點。
- 計算層:對每個分解節點循環三種計算提示,生成 9 個節點。
- 驗證層:對每個計算節點循環三種驗證提示,生成 27 個節點。
- 匯總層:選擇最高分節點,生成最終答案。
- 保存:將圖結構存為 JSON。
完整代碼
import torch
from transformers import AutoModelForCausalLM, AutoTokenizer
import uuid
import json# 模型路徑
model_path = "/opt/chenrui/qwq32b/base_model/qwq_32b"# 加載分詞器和模型
tokenizer = AutoTokenizer.from_pretrained(model_path)
model = AutoModelForCausalLM.from_pretrained(model_path)
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
model.to(device)
model.eval()# 定義評分函數(基于合理性和完整性模擬評分)
def score_thought(thought_text, original_input):keywords = ["原價", "折扣", "運費", "優惠券", "最終價格"]score = sum(1 for keyword in keywords if keyword in thought_text) # 關鍵詞越多得分越高return score + len(thought_text) / 100 # 結合內容長度# 生成思想節點
def generate_thought(input_text, prompt, max_new_tokens=100):full_prompt = f"{prompt}\n輸入: {input_text}"inputs = tokenizer(full_prompt, return_tensors="pt").to(device)outputs = model.generate(**inputs, max_new_tokens=max_new_tokens, num_return_sequences=1)return tokenizer.decode(outputs[0], skip_special_tokens=True)# 復雜 GoT 主邏輯
def complex_graph_of_thoughts(input_text):thought_graph = {"nodes": [], "edges": []}# 初始節點:問題陳述initial_node_id = str(uuid.uuid4())thought_graph["nodes"].append({"id": initial_node_id,"text": input_text,"score": score_thought(input_text, input_text)})# 第一層:分解問題decompose_prompts = ["將問題分解為計算步驟:","識別問題的關鍵變量:","列出可能的計算順序:"]for prompt in decompose_prompts:thought = generate_thought(input_text, prompt)node_id = str(uuid.uuid4())thought_graph["nodes"].append({"id": node_id, "text": thought, "score": score_thought(thought, input_text)})thought_graph["edges"].append({"from": initial_node_id, "to": node_id})# 第二層:基于分解結果計算compute_prompts = ["計算折扣后的價格:","加上運費后的總價:","考慮優惠券后的最終價格:"]first_layer_nodes = [n["id"] for n in thought_graph["nodes"][1:4]] # 取分解層節點for parent_id in first_layer_nodes:parent_text = next(n["text"] for n in thought_graph["nodes"] if n["id"] == parent_id)for prompt in compute_prompts:thought = generate_thought(parent_text, prompt)node_id = str(uuid.uuid4())thought_graph["nodes"].append({"id": node_id, "text": thought, "score": score_thought(thought, input_text)})thought_graph["edges"].append({"from": parent_id, "to": node_id})# 第三層:驗證與優化verify_prompts = ["驗證計算結果的正確性:","優化計算過程以簡化步驟:","檢查是否有遺漏條件:"]second_layer_nodes = [n["id"] for n in thought_graph["nodes"][4:]] # 取計算層節點for parent_id in second_layer_nodes:parent_text = next(n["text"] for n in thought_graph["nodes"] if n["id"] == parent_id)for prompt in verify_prompts:thought = generate_thought(parent_text, prompt)node_id = str(uuid.uuid4())thought_graph["nodes"].append({"id": node_id, "text": thought, "score": score_thought(thought, input_text)})thought_graph["edges"].append({"from": parent_id, "to": node_id})# 第四層:匯總最佳結果best_node = max(thought_graph["nodes"], key=lambda x: x["score"])final_prompt = "綜合所有分析,給出最終答案:"final_thought = generate_thought(best_node["text"], final_prompt)final_node_id = str(uuid.uuid4())thought_graph["nodes"].append({"id": final_node_id, "text": final_thought, "score": score_thought(final_thought, input_text)})thought_graph["edges"].append({"from": best_node["id"], "to": final_node_id})# 保存結果with open("complex_thought_graph.json", "w", encoding="utf-8") as f:json.dump(thought_graph, f, ensure_ascii=False, indent=2)return thought_graph# 測試復雜問題
input_text = "一個商店的商品原價是100元,打八折后賣出,再加上10元的運費,問最終價格是多少?如果顧客有20元的優惠券,最終支付多少?"
result = complex_graph_of_thoughts(input_text)
print("復雜思想圖已保存到 complex_thought_graph.json")# 輸出最佳思想
best_thought = max(result["nodes"], key=lambda x: x["score"])
print(f"最佳思想: {best_thought['text']} (得分: {best_thought['score']})")
- 多層次結構:
- 初始層:問題陳述。
- 第一層:分解問題(3個節點)。
- 第二層:基于分解結果計算(每個第一層節點衍生3個計算節點,共9個)。
- 第三層:驗證與優化(每個第二層節點衍生3個驗證節點,共27個)。
- 第四層:匯總最佳結果(1個節點)。
- 總計約 40 個節點,體現復雜性。
- 非線性推理:
- 不同分支并行探索(分解、計算、驗證)。
- 依賴關系清晰(例如計算依賴分解,驗證依賴計算)。
- 評分機制:
- 改進評分函數,結合關鍵詞匹配和內容長度,模擬對答案合理性的評估。
運行結果
最佳思想: 綜合所有分析,給出最終答案:
輸入: 檢查是否有遺漏條件:
輸入: 加上運費后的總價:
輸入: 識別問題的關鍵變量:
輸入: 一個商店的商品原價是100元,打八折后賣出,再加上10元的運費,問最終價格是多少?如果顧客有20元的優惠券,最終支付多少? 該問題的關鍵變量包括:1. **原價**:商品的原始價格為100元。
2. **折扣率**:打八折,即商品價格的80%。
3. **運費**:額外收取的10元費用。
4. **優惠券**:顧客可使用的20元優惠券。需要分步驟計算:
- 首先計算折扣后的商品價格:100元 × 0.8 = 80元。
- 然后加上運費:80元 + 10元 = 90元。
- 最后應用優惠券:90元 - 20元 = 70元。因此,最終支付金額為70元。需要注意優惠券的使用順序是否會影響結果,但通常優惠券在總價基礎上抵扣,因此上述計算合理。
如果問題中優惠券的使用條件或順序有特別說明(例如是否在運費前使用),需要根據具體條件調整計算步驟。但根據常規情況,上述解答是正確的。 您的分析基本正確,但需要確認以下幾點以確保沒有遺漏條件:
### 1. **優惠券的使用條件**
? ?- 優惠券是否僅適用于商品價格(折扣后價格),還是也包括運費?
? ?- 如果優惠券不能用于運費,那么計算方式可能不同:
? ? ?- 商品折扣后價格:80元
? ? ?- 運費:10元(不可用優惠券)
? ? ?- 優惠券抵扣商品部分:80元 - 20元 = 60元
? ? ?- 最終總價:60元(商品) + 10元(運費) = **70元**(結果不變,但邏輯不同)。### 2. **優惠券的使用順序**
? ?- 是否有規定優惠券必須在運費前使用?通常優惠券是抵扣 (得分: 12.51)
生成complex_thought_graph.json
GOT 可視化
將?complex_thought_graph.json 轉為圖片
import networkx as nx
import matplotlib.pyplot as plt
import json# 設置支持中文的字體
plt.rcParams['font.sans-serif'] = ['SimHei'] # Windows
plt.rcParams['axes.unicode_minus'] = False# 讀取 JSON 數據
with open("complex_thought_graph.json", "r", encoding="utf-8") as f:data = json.load(f)# 創建有向圖
G = nx.DiGraph()# 添加節點
for node in data["nodes"]:label = f"{node['text'][:10]}...\nScore: {node['score']:.2f}"G.add_node(node["id"], label=label)# 添加邊
for edge in data["edges"]:G.add_edge(edge["from"], edge["to"])# 設置布局
pos = nx.spring_layout(G)# 繪制并保存
plt.figure(figsize=(20, 15)) # 增大畫布以容納更多節點
nx.draw(G, pos, with_labels=True, labels=nx.get_node_attributes(G, "label"),node_size=2000, node_color="lightblue", font_size=6, font_weight="bold",arrows=True, arrowstyle="->", arrowsize=15)plt.title("Complex Graph of Thoughts Visualization", fontsize=16)
plt.savefig("complex_thought_graph.png", dpi=300, bbox_inches="tight")
print("圖已保存為 complex_thought_graph.png")
- 節點數量:約40個節點。
- 推理深度:4層結構,包含分解、計算、驗證和匯總。
- 多樣性:多個分支探索不同角度(例如計算順序、驗證方法)。
- 結果優化:通過評分選擇最佳路徑并匯總。
節點太多了看不清了,用ds.js 渲染個拖拉拽的html
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Interactive Graph of Thoughts</title><style>html, body {margin: 0;padding: 0;height: 100vh;overflow: hidden;}svg {width: 100%;height: 100%;display: block;}.node circle {fill: lightblue;stroke: #333;stroke-width: 1.5px;}.node text {font-size: 12px;pointer-events: none;}.link {stroke: #999;stroke-opacity: 0.6;}#error {color: red;font-family: Arial, sans-serif;position: absolute;top: 10px;left: 10px;}</style>
</head>
<body><svg></svg><div id="error"></div><script src="https://d3js.org/d3.v7.min.js"></script><script>// 設置 SVG 尺寸const svg = d3.select("svg");let width = window.innerWidth;let height = window.innerHeight;// 讀取 JSON 文件fetch('./complex_thought_graph.json').then(response => {if (!response.ok) {throw new Error(`HTTP error! Status: ${response.status}`);}return response.json();}).then(data => {if (!data.nodes || !data.edges) {throw new Error("Invalid JSON format: missing 'nodes' or 'edges'");}// 準備數據const nodes = data.nodes.map(node => ({id: node.id,text: node.text.slice(0, 10) + '...',score: node.score}));const links = data.edges.map(edge => ({source: edge.from,target: edge.to}));// 初始化力導向圖const simulation = d3.forceSimulation(nodes).force("link", d3.forceLink(links).id(d => d.id).distance(100)).force("charge", d3.forceManyBody().strength(-200)).force("center", d3.forceCenter(width / 2, height / 2));// 繪制邊const link = svg.append("g").attr("class", "links").selectAll("line").data(links).enter().append("line").attr("class", "link");// 繪制節點const node = svg.append("g").attr("class", "nodes").selectAll("g").data(nodes).enter().append("g").attr("class", "node").call(d3.drag().on("start", dragstarted).on("drag", dragged).on("end", dragended));node.append("circle").attr("r", 10);node.append("text").attr("dx", 12).attr("dy", ".35em").text(d => `${d.text} (Score: ${d.score.toFixed(2)})`);// 力仿真更新simulation.on("tick", () => {link.attr("x1", d => d.source.x).attr("y1", d => d.source.y).attr("x2", d => d.target.x).attr("y2", d => d.target.y);node.attr("transform", d => `translate(${d.x},${d.y})`);});// 拖拽函數function dragstarted(event, d) {if (!event.active) simulation.alphaTarget(0.3).restart();d.fx = d.x;d.fy = d.y;}function dragged(event, d) {d.fx = event.x;d.fy = event.y;}function dragended(event, d) {if (!event.active) simulation.alphaTarget(0);d.fx = null;d.fy = null;}// 窗口大小調整window.addEventListener('resize', () => {width = window.innerWidth;height = window.innerHeight;svg.attr("width", width).attr("height", height);simulation.force("center", d3.forceCenter(width / 2, height / 2));simulation.alpha(1).restart();});}).catch(error => {console.error('Error loading JSON:', error);d3.select("#error").text(`Error loading JSON: ${error.message}`);});</script>
</body>
</html>
python -m http.server 8000
查看下GOT結構