<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>呓语笔记</title>
  
  
  <link href="https://www.helloyiyu.com/atom.xml" rel="self"/>
  
  <link href="https://www.helloyiyu.com/"/>
  <updated>2025-12-22T15:31:03.668Z</updated>
  <id>https://www.helloyiyu.com/</id>
  
  <author>
    <name>HelloYiYu</name>
    
  </author>
  
  <generator uri="https://hexo.io/">Hexo</generator>
  
  <entry>
    <title>微调实践笔记</title>
    <link href="https://www.helloyiyu.com/3321999099.html"/>
    <id>https://www.helloyiyu.com/3321999099.html</id>
    <published>2025-12-22T07:29:29.000Z</published>
    <updated>2025-12-22T15:31:03.668Z</updated>
    
    <content type="html"><![CDATA[<h1 id="Qwen3-微调实践笔记"><a href="#Qwen3-微调实践笔记" class="headerlink" title="Qwen3 微调实践笔记"></a>Qwen3 微调实践笔记</h1><h2 id="一、模型微调的意义"><a href="#一、模型微调的意义" class="headerlink" title="一、模型微调的意义"></a>一、模型微调的意义</h2><h3 id="为什么要微调"><a href="#为什么要微调" class="headerlink" title="为什么要微调"></a>为什么要微调</h3><p><strong>1. 降低训练成本与门槛</strong></p><ul><li>大模型参数量巨大，从头训练成本极高，对企业而言性价比低。</li><li>微调预训练模型是更经济高效的解决方案。</li></ul><p><strong>2. 突破 Prompt Engineering 的限制</strong></p><p>Prompt Engineering 虽易于上手，但存在明显缺点：</p><ul><li><strong>长度限制</strong>：输入序列长度受限，长 Prompt 会被截断，影响输出质量。</li><li><strong>推理成本高</strong>：推理成本与 Prompt 长度的平方正相关。</li></ul><p><strong>3. 提升特定领域性能</strong></p><ul><li>当企业拥有高质量的<strong>自有领域数据</strong>，且 Prompt Engineering 效果不达预期时，微调能显著提升模型在特定领域的专业能力。</li></ul><p><strong>4. 实现个性化服务</strong></p><ul><li>针对不同用户的数据，训练轻量级的微调模型，是实现个性化服务的有效方案。</li></ul><p><strong>5. 保障数据安全</strong></p><ul><li>当数据因安全或合规要求不能传递给第三方服务时，必须搭建自有模型。</li><li>开源大模型通常需要结合自有数据进行微调，才能满足具体业务需求。</li></ul><h3 id="微调-vs-检索增强生成-RAG"><a href="#微调-vs-检索增强生成-RAG" class="headerlink" title="微调 vs. 检索增强生成 (RAG)"></a>微调 vs. 检索增强生成 (RAG)</h3><p>与纯RAG系统相比，微调具备以下优势：</p><ul><li><strong>能力范围</strong>：微调几乎可以实现 RAG 的所有功能，但反之不成立。</li><li><strong>知识内化</strong>：微调将外部知识直接嵌入模型权重，使模型能独立处理特定领域查询，无需依赖外部检索系统。</li><li><strong>混合方案</strong>：即使在微调与RAG并用的混合架构中，微调后的模型也能提供可靠的<strong>后备方案</strong>，增强系统鲁棒性。</li></ul><h3 id="微调的优缺点与限制"><a href="#微调的优缺点与限制" class="headerlink" title="微调的优缺点与限制"></a>微调的优缺点与限制</h3><p>参考一个案例：<a href="https://www.53ai.com/news/finetuning/2025062093260.html">大模型微调，为什么99%的企业都不应该碰这个坑？</a></p><p>微调是通过调整预训练模型参数，使其适应特定任务或领域需求的方法。</p><p><strong>主要优点：</strong></p><ul><li><strong>高效灵活</strong>：通常只需较少的数据样本即可获得良好性能，尤其适合特定领域任务（如提升对专有词汇的理解）。</li><li><strong>部署简便</strong>：模型可直接部署，无需额外外部组件，适合对实时性要求高的场景。</li></ul><p><strong>主要缺点与限制：</strong></p><ul><li><strong>资源消耗大</strong>：虽然不及全量训练，但微调也需要大量计算资源和时间，训练成本高。</li><li><strong>过拟合与灾难性遗忘</strong>：模型可能在新领域表现良好，但遗忘原有通用知识。</li><li><strong>泛化能力可能受限</strong>：在某些复杂任务上，其泛化能力可能不如RAG等方法。</li><li><strong>更新不灵活</strong>：对于需要频繁更新知识的场景，微调需重新训练，不如RAG（仅更新知识库）灵活。</li></ul><h2 id="二、Unsloth-工具介绍"><a href="#二、Unsloth-工具介绍" class="headerlink" title="二、Unsloth 工具介绍"></a>二、Unsloth 工具介绍</h2><p>Unsloth 是一个专为大语言模型优化的高效微调框架，具有以下核心特点：</p><ul><li><strong>更快的训练速度</strong>：比标准 LoRA 训练快 <strong>2-5 倍</strong>。其原理是通过<strong>算子融合</strong>等技术，减少GPU间的数据传输开销。</li><li><strong>更低的内存占用</strong>：采用<strong>梯度检查点</strong>技术，在前向传播时不保存全部中间结果，需时重新计算，从而减少约 <strong>30%</strong> 的 VRAM 使用。</li><li><strong>优化的 LoRA 实现</strong>：针对当前主流的 LoRA 微调方法进行了底层深度优化。</li><li><strong>广泛的模型支持</strong>：支持 Llama、Mistral、<strong>Qwen</strong>、Phi 等多种主流开源大模型。</li></ul><h2 id="三、准备工作"><a href="#三、准备工作" class="headerlink" title="三、准备工作"></a>三、准备工作</h2><h3 id="3-1-环境准备"><a href="#3-1-环境准备" class="headerlink" title="3.1 环境准备"></a>3.1 环境准备</h3><ol><li>安装基础的 GPU 驱动、CUDA等。</li><li>在 Python 虚拟环境中安装必要的依赖包：</li></ol><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">pip install swanlab modelscope==1.22.0 &quot;transformers&gt;=4.50.0&quot; datasets==3.2.0 accelerate pandas addict</span><br><span class="line">pip install &quot;unsloth[colab-new]&quot; -i https://pypi.tuna.tsinghua.edu.cn/simple</span><br></pre></td></tr></table></figure><h3 id="3-2-下载基座模型"><a href="#3-2-下载基座模型" class="headerlink" title="3.2 下载基座模型"></a>3.2 下载基座模型</h3><p>本例选用 <strong>Qwen3-8B</strong> 模型。</p><ul><li>模型主页：<a href="https://www.modelscope.cn/models/Qwen/Qwen3-8B">https://www.modelscope.cn/models/Qwen/Qwen3-8B</a></li></ul><p>下载命令：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">(ai) yiyu@ubuntu:~/nfs/workspace/qwen3_08b_tunning$ git clone https://www.modelscope.cn/Qwen/Qwen3-8B.git</span><br><span class="line">Cloning into &#x27;Qwen3-8B&#x27;...</span><br><span class="line">remote: Enumerating objects: 53, done.</span><br><span class="line">remote: Total 53 (delta 0), reused 0 (delta 0), pack-reused 53</span><br><span class="line">Receiving objects: 100% (53/53), 1.73 MiB | 11.67 MiB/s, done.</span><br><span class="line">Resolving deltas: 100% (15/15), done.</span><br><span class="line">Updating files: 100% (16/16), done.</span><br><span class="line">Filtering content: 100% (6/6), 15.26 GiB | 21.42 MiB/s, done.</span><br></pre></td></tr></table></figure><h3 id="3-3-下载数据集"><a href="#3-3-下载数据集" class="headerlink" title="3.3 下载数据集"></a>3.3 下载数据集</h3><p>本例选用魔塔社区开源的 <strong>HUST-Student-Handbook</strong>（华中科技大学学生手册）数据集。</p><ul><li>数据集主页：<a href="https://www.modelscope.cn/datasets/alleyf/HUST-Student-Handbook">https://www.modelscope.cn/datasets/alleyf/HUST-Student-Handbook</a></li></ul><p>下载命令：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">(ai) yiyu@ubuntu:~/nfs/workspace/qwen3_08b_tunning$ git clone https://www.modelscope.cn/datasets/alleyf/HUST-Student-Handbook.git</span><br><span class="line">Cloning into &#x27;HUST-Student-Handbook&#x27;...</span><br><span class="line">remote: Enumerating objects: 32, done.</span><br><span class="line">remote: Counting objects: 100% (32/32), done.</span><br><span class="line">remote: Compressing objects: 100% (32/32), done.</span><br><span class="line">remote: Total 32 (delta 12), reused 0 (delta 0), pack-reused 0</span><br><span class="line">Receiving objects: 100% (32/32), 12.25 KiB | 896.00 KiB/s, done.</span><br><span class="line">Resolving deltas: 100% (12/12), done.</span><br><span class="line">Filtering content: 100% (2/2), 527.32 KiB | 318.00 KiB/s, done.</span><br></pre></td></tr></table></figure><h2 id="四、训练过程"><a href="#四、训练过程" class="headerlink" title="四、训练过程"></a>四、训练过程</h2><h3 id="4-1-训练脚本"><a href="#4-1-训练脚本" class="headerlink" title="4.1 训练脚本"></a>4.1 训练脚本</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br><span class="line">196</span><br><span class="line">197</span><br><span class="line">198</span><br><span class="line">199</span><br><span class="line">200</span><br><span class="line">201</span><br><span class="line">202</span><br><span class="line">203</span><br><span class="line">204</span><br><span class="line">205</span><br><span class="line">206</span><br><span class="line">207</span><br><span class="line">208</span><br><span class="line">209</span><br><span class="line">210</span><br><span class="line">211</span><br><span class="line">212</span><br><span class="line">213</span><br><span class="line">214</span><br><span class="line">215</span><br><span class="line">216</span><br><span class="line">217</span><br><span class="line">218</span><br><span class="line">219</span><br><span class="line">220</span><br><span class="line">221</span><br><span class="line">222</span><br><span class="line">223</span><br><span class="line">224</span><br><span class="line">225</span><br><span class="line">226</span><br><span class="line">227</span><br><span class="line">228</span><br><span class="line">229</span><br><span class="line">230</span><br><span class="line">231</span><br><span class="line">232</span><br><span class="line">233</span><br><span class="line">234</span><br><span class="line">235</span><br><span class="line">236</span><br><span class="line">237</span><br><span class="line">238</span><br><span class="line">239</span><br><span class="line">240</span><br><span class="line">241</span><br><span class="line">242</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">#!/usr/bin/env python3</span></span><br><span class="line"><span class="string">&quot;&quot;&quot;</span></span><br><span class="line"><span class="string">Qwen3-8B 微调脚本</span></span><br><span class="line"><span class="string">&quot;&quot;&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> os</span><br><span class="line"><span class="keyword">import</span> sys</span><br><span class="line"><span class="keyword">import</span> torch</span><br><span class="line"></span><br><span class="line"><span class="comment"># 设置环境变量 - 必须在导入之前</span></span><br><span class="line">os.environ[<span class="string">&#x27;UNSLOTH_NO_STATISTICS&#x27;</span>] = <span class="string">&#x27;1&#x27;</span>  <span class="comment"># 禁用Unsloth统计信息收集，减少内存占用</span></span><br><span class="line">os.environ[<span class="string">&#x27;HF_HUB_OFFLINE&#x27;</span>] = <span class="string">&#x27;1&#x27;</span>         <span class="comment"># 强制使用本地缓存，避免从HuggingFace Hub下载</span></span><br><span class="line">os.environ[<span class="string">&#x27;TRANSFORMERS_OFFLINE&#x27;</span>] = <span class="string">&#x27;1&#x27;</span>   <span class="comment"># 强制使用本地缓存，避免从HuggingFace Hub下载</span></span><br><span class="line">os.environ[<span class="string">&#x27;WANDB_MODE&#x27;</span>] = <span class="string">&#x27;disabled&#x27;</span>      <span class="comment"># 禁用Weights &amp; Biases日志记录，避免登录问题</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 现在导入其他包</span></span><br><span class="line"><span class="keyword">import</span> json</span><br><span class="line"><span class="keyword">from</span> datasets <span class="keyword">import</span> load_dataset</span><br><span class="line"><span class="keyword">from</span> unsloth <span class="keyword">import</span> FastLanguageModel</span><br><span class="line"><span class="keyword">from</span> transformers <span class="keyword">import</span> TrainingArguments</span><br><span class="line"><span class="keyword">from</span> trl <span class="keyword">import</span> SFTTrainer</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">format_conversation</span>(<span class="params">example</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;格式化对话数据 - 适配Qwen格式&quot;&quot;&quot;</span></span><br><span class="line">    conversations = example[<span class="string">&quot;conversations&quot;</span>]</span><br><span class="line">    </span><br><span class="line">    formatted_text = <span class="string">&quot;&quot;</span></span><br><span class="line">    <span class="keyword">for</span> msg <span class="keyword">in</span> conversations:</span><br><span class="line">        <span class="keyword">if</span> msg[<span class="string">&quot;role&quot;</span>] == <span class="string">&quot;user&quot;</span>:</span><br><span class="line">            formatted_text += <span class="string">f&quot;&lt;|im_start|&gt;user\n<span class="subst">&#123;msg[<span class="string">&#x27;content&#x27;</span>]&#125;</span>&lt;|im_end|&gt;\n&quot;</span></span><br><span class="line">        <span class="keyword">elif</span> msg[<span class="string">&quot;role&quot;</span>] == <span class="string">&quot;assistant&quot;</span>:</span><br><span class="line">            formatted_text += <span class="string">f&quot;&lt;|im_start|&gt;assistant\n<span class="subst">&#123;msg[<span class="string">&#x27;content&#x27;</span>]&#125;</span>&lt;|im_end|&gt;\n&quot;</span></span><br><span class="line">    </span><br><span class="line">    <span class="keyword">return</span> &#123;<span class="string">&quot;text&quot;</span>: formatted_text&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">main</span>():</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot;=&quot;</span> * <span class="number">60</span>)</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot;Qwen3-8B 微调&quot;</span>)</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot;=&quot;</span> * <span class="number">60</span>)</span><br><span class="line">    </span><br><span class="line">    <span class="comment"># 模型参数</span></span><br><span class="line">    model_path = <span class="string">&quot;/home/yiyu/nfs/workspace/qwen3_08b_tunning/Qwen3-8B&quot;</span></span><br><span class="line">    train_file = <span class="string">&quot;/home/yiyu/nfs/workspace/qwen3_08b_tunning/HUST-Student-Handbook/lora_hust_student_handbookt.jsonl&quot;</span></span><br><span class="line">    eval_file = <span class="string">&quot;&quot;</span></span><br><span class="line">    output_dir = <span class="string">&quot;/home/yiyu/nfs/workspace/qwen3_08b_tunning/qwen3-8b-finetuned&quot;</span></span><br><span class="line">    </span><br><span class="line">    <span class="comment"># 检查文件是否存在</span></span><br><span class="line">    <span class="keyword">if</span> <span class="keyword">not</span> os.path.exists(model_path):</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">f&quot;错误: 模型路径不存在: <span class="subst">&#123;model_path&#125;</span>&quot;</span>)</span><br><span class="line">        sys.exit(<span class="number">1</span>)</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">if</span> <span class="keyword">not</span> os.path.exists(train_file):</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">f&quot;错误: 训练文件不存在: <span class="subst">&#123;train_file&#125;</span>&quot;</span>)</span><br><span class="line">        sys.exit(<span class="number">1</span>)</span><br><span class="line">    </span><br><span class="line">    <span class="comment"># 检查GPU</span></span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&quot;\nGPU信息:&quot;</span>)</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&quot;  CUDA可用: <span class="subst">&#123;torch.cuda.is_available()&#125;</span>&quot;</span>)</span><br><span class="line">    <span class="keyword">if</span> torch.cuda.is_available():</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">f&quot;  GPU名称: <span class="subst">&#123;torch.cuda.get_device_name(<span class="number">0</span>)&#125;</span>&quot;</span>)</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">f&quot;  GPU内存: <span class="subst">&#123;torch.cuda.get_device_properties(<span class="number">0</span>).total_memory / <span class="number">1024</span>**<span class="number">3</span>:<span class="number">.1</span>f&#125;</span> GB&quot;</span>)</span><br><span class="line">    </span><br><span class="line">    <span class="comment"># 1. 加载模型和分词器</span></span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot;\n1. 加载模型和分词器...&quot;</span>)</span><br><span class="line">    <span class="keyword">try</span>:</span><br><span class="line">        model, tokenizer = FastLanguageModel.from_pretrained(</span><br><span class="line">            model_name=model_path,      <span class="comment"># 预训练模型路径或HuggingFace模型ID</span></span><br><span class="line">            max_seq_length=<span class="number">2048</span>,        <span class="comment"># 模型支持的最大序列长度</span></span><br><span class="line">            dtype=<span class="literal">None</span>,                 <span class="comment"># 数据类型，None表示自动检测（通常是torch.float16）</span></span><br><span class="line">            load_in_4bit=<span class="literal">True</span>,          <span class="comment"># 使用4位量化加载模型，减少内存占用</span></span><br><span class="line">            token=<span class="literal">None</span>,                 <span class="comment"># HuggingFace访问令牌，None表示不使用或已有缓存</span></span><br><span class="line">        )</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">f&quot;  ✓ 模型加载成功&quot;</span>)</span><br><span class="line">    <span class="keyword">except</span> Exception <span class="keyword">as</span> e:</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">f&quot;  ✗ 模型加载失败: <span class="subst">&#123;e&#125;</span>&quot;</span>)</span><br><span class="line">        sys.exit(<span class="number">1</span>)</span><br><span class="line">    </span><br><span class="line">    <span class="comment"># 设置tokenizer</span></span><br><span class="line">    tokenizer.pad_token = tokenizer.eos_token  <span class="comment"># 使用EOS令牌作为填充令牌</span></span><br><span class="line">    tokenizer.padding_side = <span class="string">&quot;right&quot;</span>           <span class="comment"># 在序列右侧进行填充</span></span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&quot;  ✓ Tokenizer设置完成&quot;</span>)</span><br><span class="line">    </span><br><span class="line">    <span class="comment"># 2. 应用LoRA</span></span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot;\n2. 应用LoRA配置...&quot;</span>)</span><br><span class="line">    <span class="keyword">try</span>:</span><br><span class="line">        model = FastLanguageModel.get_peft_model(</span><br><span class="line">            model,                          <span class="comment"># 基础模型</span></span><br><span class="line">            r=<span class="number">16</span>,                           <span class="comment"># LoRA秩，控制低秩矩阵的维度，值越小参数越少</span></span><br><span class="line">            target_modules=[                <span class="comment"># 要应用LoRA的目标模块列表</span></span><br><span class="line">                <span class="string">&quot;q_proj&quot;</span>, <span class="string">&quot;k_proj&quot;</span>, <span class="string">&quot;v_proj&quot;</span>, <span class="string">&quot;o_proj&quot;</span>,  <span class="comment"># 注意力机制投影层</span></span><br><span class="line">                <span class="string">&quot;gate_proj&quot;</span>, <span class="string">&quot;up_proj&quot;</span>, <span class="string">&quot;down_proj&quot;</span>,     <span class="comment"># FFN层投影</span></span><br><span class="line">            ],</span><br><span class="line">            lora_alpha=<span class="number">32</span>,                  <span class="comment"># LoRA缩放因子，控制新权重对原始权重的贡献程度</span></span><br><span class="line">            lora_dropout=<span class="number">0.05</span>,              <span class="comment"># LoRA层的Dropout率，防止过拟合</span></span><br><span class="line">            bias=<span class="string">&quot;none&quot;</span>,                    <span class="comment"># 偏置训练策略：&quot;none&quot;不训练，&quot;all&quot;训练所有偏置</span></span><br><span class="line">            use_gradient_checkpointing=<span class="literal">True</span>, <span class="comment"># 使用梯度检查点，减少内存占用但增加计算时间</span></span><br><span class="line">            random_state=<span class="number">42</span>,                <span class="comment"># 随机种子，确保可重复性</span></span><br><span class="line">            use_rslora=<span class="literal">False</span>,               <span class="comment"># 是否使用rsLoRA（减少梯度的LoRA变体）</span></span><br><span class="line">            loftq_config=<span class="literal">None</span>,              <span class="comment"># LoftQ配置，用于量化感知微调</span></span><br><span class="line">        )</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">f&quot;  ✓ LoRA应用成功 (r=16, alpha=32)&quot;</span>)</span><br><span class="line">    <span class="keyword">except</span> Exception <span class="keyword">as</span> e:</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">f&quot;  ✗ LoRA应用失败: <span class="subst">&#123;e&#125;</span>&quot;</span>)</span><br><span class="line">        sys.exit(<span class="number">1</span>)</span><br><span class="line">    </span><br><span class="line">    <span class="comment"># 计算可训练参数</span></span><br><span class="line">    trainable_params = <span class="built_in">sum</span>(p.numel() <span class="keyword">for</span> p <span class="keyword">in</span> model.parameters() <span class="keyword">if</span> p.requires_grad)</span><br><span class="line">    total_params = <span class="built_in">sum</span>(p.numel() <span class="keyword">for</span> p <span class="keyword">in</span> model.parameters())</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&quot;  ✓ 可训练参数: <span class="subst">&#123;trainable_params:,&#125;</span> (占总参数 <span class="subst">&#123;<span class="number">100</span>*trainable_params/total_params:<span class="number">.2</span>f&#125;</span>%)&quot;</span>)</span><br><span class="line">    </span><br><span class="line">    <span class="comment"># 3. 加载数据</span></span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot;\n3. 加载和准备数据...&quot;</span>)</span><br><span class="line">    <span class="keyword">try</span>:</span><br><span class="line">        <span class="comment"># 加载训练数据</span></span><br><span class="line">        train_dataset = load_dataset(<span class="string">&quot;json&quot;</span>, data_files=train_file, split=<span class="string">&quot;train&quot;</span>)</span><br><span class="line">        train_dataset = train_dataset.<span class="built_in">map</span>(</span><br><span class="line">            format_conversation,</span><br><span class="line">            remove_columns=[<span class="string">&quot;conversations&quot;</span>]</span><br><span class="line">        )</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">f&quot;  ✓ 训练数据: <span class="subst">&#123;<span class="built_in">len</span>(train_dataset)&#125;</span> 条样本&quot;</span>)</span><br><span class="line">        </span><br><span class="line">        <span class="comment"># 加载评估数据（如果有）</span></span><br><span class="line">        <span class="keyword">if</span> os.path.exists(eval_file):</span><br><span class="line">            eval_dataset = load_dataset(<span class="string">&quot;json&quot;</span>, data_files=eval_file, split=<span class="string">&quot;train&quot;</span>)</span><br><span class="line">            eval_dataset = eval_dataset.<span class="built_in">map</span>(</span><br><span class="line">                format_conversation,</span><br><span class="line">                remove_columns=[<span class="string">&quot;conversations&quot;</span>]</span><br><span class="line">            )</span><br><span class="line">            <span class="built_in">print</span>(<span class="string">f&quot;  ✓ 评估数据: <span class="subst">&#123;<span class="built_in">len</span>(eval_dataset)&#125;</span> 条样本&quot;</span>)</span><br><span class="line">            eval_data_available = <span class="literal">True</span></span><br><span class="line">        <span class="keyword">else</span>:</span><br><span class="line">            <span class="built_in">print</span>(<span class="string">f&quot;  ⓘ 评估数据不存在，跳过评估&quot;</span>)</span><br><span class="line">            eval_dataset = <span class="literal">None</span></span><br><span class="line">            eval_data_available = <span class="literal">False</span></span><br><span class="line">            </span><br><span class="line">    <span class="keyword">except</span> Exception <span class="keyword">as</span> e:</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">f&quot;  ✗ 数据加载失败: <span class="subst">&#123;e&#125;</span>&quot;</span>)</span><br><span class="line">        sys.exit(<span class="number">1</span>)</span><br><span class="line">    </span><br><span class="line">    <span class="comment"># 4. 设置训练参数 - 使用兼容的参数名</span></span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot;\n4. 设置训练参数...&quot;</span>)</span><br><span class="line">    <span class="keyword">try</span>:</span><br><span class="line">        <span class="comment"># 创建输出目录</span></span><br><span class="line">        os.makedirs(output_dir, exist_ok=<span class="literal">True</span>)</span><br><span class="line">        </span><br><span class="line">        <span class="comment"># 训练参数配置</span></span><br><span class="line">        training_args = TrainingArguments(</span><br><span class="line">            output_dir=output_dir,                    <span class="comment"># 模型和日志输出目录</span></span><br><span class="line">            num_train_epochs=<span class="number">3</span>,                      <span class="comment"># 训练轮数</span></span><br><span class="line">            per_device_train_batch_size=<span class="number">2</span>,           <span class="comment"># 每个设备/GPU的训练批次大小</span></span><br><span class="line">            gradient_accumulation_steps=<span class="number">4</span>,           <span class="comment"># 梯度累积步数，模拟更大批次</span></span><br><span class="line">            warmup_ratio=<span class="number">0.03</span>,                       <span class="comment"># 学习率预热比例（占总训练步数的比例）</span></span><br><span class="line">            learning_rate=<span class="number">2e-4</span>,                      <span class="comment"># 初始学习率</span></span><br><span class="line">            fp16=<span class="literal">True</span>,                               <span class="comment"># 使用混合精度训练（16位浮点数）</span></span><br><span class="line">            logging_steps=<span class="number">10</span>,                        <span class="comment"># 每多少步记录一次日志</span></span><br><span class="line">            save_strategy=<span class="string">&quot;steps&quot;</span>,                   <span class="comment"># 模型保存策略：&quot;steps&quot;按步数保存</span></span><br><span class="line">            save_steps=<span class="number">100</span>,                          <span class="comment"># 每多少步保存一次模型</span></span><br><span class="line">            eval_strategy=<span class="string">&quot;steps&quot;</span> <span class="keyword">if</span> eval_data_available <span class="keyword">else</span> <span class="string">&quot;no&quot;</span>,  <span class="comment"># 评估策略</span></span><br><span class="line">            eval_steps=<span class="number">100</span> <span class="keyword">if</span> eval_data_available <span class="keyword">else</span> <span class="literal">None</span>,         <span class="comment"># 每多少步评估一次</span></span><br><span class="line">            gradient_checkpointing=<span class="literal">True</span>,             <span class="comment"># 使用梯度检查点，减少内存占用</span></span><br><span class="line">            optim=<span class="string">&quot;adamw_8bit&quot;</span>,                      <span class="comment"># 优化器类型，8位AdamW</span></span><br><span class="line">            lr_scheduler_type=<span class="string">&quot;cosine&quot;</span>,              <span class="comment"># 学习率调度器类型：余弦退火</span></span><br><span class="line">            seed=<span class="number">42</span>,                                 <span class="comment"># 随机种子</span></span><br><span class="line">            report_to=<span class="string">&quot;none&quot;</span>,                        <span class="comment"># 禁用所有日志记录器</span></span><br><span class="line">            ddp_find_unused_parameters=<span class="literal">False</span>,        <span class="comment"># DDP训练中不查找未使用参数</span></span><br><span class="line">            remove_unused_columns=<span class="literal">False</span>,             <span class="comment"># 不自动删除数据集未使用列</span></span><br><span class="line">            save_total_limit=<span class="number">3</span>,                      <span class="comment"># 最多保存的检查点数量</span></span><br><span class="line">            load_best_model_at_end=<span class="literal">True</span> <span class="keyword">if</span> eval_data_available <span class="keyword">else</span> <span class="literal">False</span>,  <span class="comment"># 训练结束时加载最佳模型</span></span><br><span class="line">            metric_for_best_model=<span class="string">&quot;loss&quot;</span> <span class="keyword">if</span> eval_data_available <span class="keyword">else</span> <span class="literal">None</span>,  <span class="comment"># 评估指标</span></span><br><span class="line">            greater_is_better=<span class="literal">False</span> <span class="keyword">if</span> eval_data_available <span class="keyword">else</span> <span class="literal">None</span>,       <span class="comment"># 指标是否越大越好</span></span><br><span class="line">        )</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">f&quot;  ✓ 训练参数设置完成&quot;</span>)</span><br><span class="line">    <span class="keyword">except</span> Exception <span class="keyword">as</span> e:</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">f&quot;  ✗ 训练参数设置失败: <span class="subst">&#123;e&#125;</span>&quot;</span>)</span><br><span class="line">        sys.exit(<span class="number">1</span>)</span><br><span class="line">    </span><br><span class="line">    <span class="comment"># 5. 创建训练器</span></span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot;\n5. 创建训练器...&quot;</span>)</span><br><span class="line">    <span class="keyword">try</span>:</span><br><span class="line">        trainer = SFTTrainer(</span><br><span class="line">            model=model,                          <span class="comment"># 要训练的模型</span></span><br><span class="line">            tokenizer=tokenizer,                  <span class="comment"># 分词器</span></span><br><span class="line">            train_dataset=train_dataset,          <span class="comment"># 训练数据集</span></span><br><span class="line">            eval_dataset=eval_dataset,            <span class="comment"># 评估数据集</span></span><br><span class="line">            dataset_text_field=<span class="string">&quot;text&quot;</span>,            <span class="comment"># 数据集中文本字段的名称</span></span><br><span class="line">            max_seq_length=<span class="number">2048</span>,                  <span class="comment"># 最大序列长度，超过部分会被截断</span></span><br><span class="line">            packing=<span class="literal">False</span>,                        <span class="comment"># 是否将多个短序列打包成一个批次</span></span><br><span class="line">            args=training_args,                   <span class="comment"># 训练参数配置</span></span><br><span class="line">        )</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">f&quot;  ✓ 训练器创建成功&quot;</span>)</span><br><span class="line">    <span class="keyword">except</span> Exception <span class="keyword">as</span> e:</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">f&quot;  ✗ 训练器创建失败: <span class="subst">&#123;e&#125;</span>&quot;</span>)</span><br><span class="line">        sys.exit(<span class="number">1</span>)</span><br><span class="line">    </span><br><span class="line">    <span class="comment"># 6. 开始训练</span></span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot;\n&quot;</span> + <span class="string">&quot;=&quot;</span> * <span class="number">60</span>)</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot;开始训练...&quot;</span>)</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot;=&quot;</span> * <span class="number">60</span>)</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">try</span>:</span><br><span class="line">        <span class="comment"># 训练模型</span></span><br><span class="line">        train_result = trainer.train()</span><br><span class="line">        </span><br><span class="line">        <span class="built_in">print</span>(<span class="string">&quot;\n&quot;</span> + <span class="string">&quot;=&quot;</span> * <span class="number">60</span>)</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">&quot;训练完成！&quot;</span>)</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">&quot;=&quot;</span> * <span class="number">60</span>)</span><br><span class="line">        </span><br><span class="line">        <span class="comment"># 打印训练统计</span></span><br><span class="line">        <span class="built_in">print</span>(<span class="string">f&quot;\n训练统计:&quot;</span>)</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">f&quot;  总训练步数: <span class="subst">&#123;train_result.global_step&#125;</span>&quot;</span>)</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">f&quot;  训练耗时: <span class="subst">&#123;train_result.metrics[<span class="string">&#x27;train_runtime&#x27;</span>]:<span class="number">.2</span>f&#125;</span> 秒&quot;</span>)</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">f&quot;  每秒步数: <span class="subst">&#123;train_result.metrics[<span class="string">&#x27;train_samples_per_second&#x27;</span>]:<span class="number">.2</span>f&#125;</span>&quot;</span>)</span><br><span class="line">        </span><br><span class="line">    <span class="keyword">except</span> Exception <span class="keyword">as</span> e:</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">f&quot;\n训练过程中出错: <span class="subst">&#123;e&#125;</span>&quot;</span>)</span><br><span class="line">        sys.exit(<span class="number">1</span>)</span><br><span class="line">    </span><br><span class="line">    <span class="comment"># 7. 保存模型</span></span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot;\n6. 保存模型...&quot;</span>)</span><br><span class="line">    <span class="keyword">try</span>:</span><br><span class="line">        <span class="comment"># 保存模型和tokenizer</span></span><br><span class="line">        trainer.save_model()</span><br><span class="line">        tokenizer.save_pretrained(output_dir)</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">f&quot;  ✓ 模型保存到: <span class="subst">&#123;output_dir&#125;</span>&quot;</span>)</span><br><span class="line">        </span><br><span class="line">        <span class="comment"># 保存训练参数</span></span><br><span class="line">        <span class="keyword">with</span> <span class="built_in">open</span>(os.path.join(output_dir, <span class="string">&quot;training_args.json&quot;</span>), <span class="string">&quot;w&quot;</span>) <span class="keyword">as</span> f:</span><br><span class="line">            json.dump(training_args.to_dict(), f, indent=<span class="number">2</span>)</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">f&quot;  ✓ 训练参数保存&quot;</span>)</span><br><span class="line">        </span><br><span class="line">    <span class="keyword">except</span> Exception <span class="keyword">as</span> e:</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">f&quot;  ✗ 模型保存失败: <span class="subst">&#123;e&#125;</span>&quot;</span>)</span><br><span class="line">        sys.exit(<span class="number">1</span>)</span><br><span class="line">    </span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot;\n&quot;</span> + <span class="string">&quot;=&quot;</span> * <span class="number">60</span>)</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot;微调流程完成！&quot;</span>)</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot;=&quot;</span> * <span class="number">60</span>)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">&quot;__main__&quot;</span>:</span><br><span class="line">    main()</span><br><span class="line"></span><br></pre></td></tr></table></figure><h3 id="4-2-微调训练过程"><a href="#4-2-微调训练过程" class="headerlink" title="4.2 微调训练过程"></a>4.2 微调训练过程</h3><p>微调训练大概占用8G显存</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line">(base) yiyu@ubuntu:~/nfs/workspace/qwen3_08b_tunning$ nvidia-smi</span><br><span class="line">Mon Dec 22 15:00:59 2025       </span><br><span class="line">+---------------------------------------------------------------------------------------+</span><br><span class="line">| NVIDIA-SMI 535.274.02             Driver Version: 535.274.02   CUDA Version: 12.2     |</span><br><span class="line">|-----------------------------------------+----------------------+----------------------+</span><br><span class="line">| GPU  Name                 Persistence-M | Bus-Id        Disp.A | Volatile Uncorr. ECC |</span><br><span class="line">| Fan  Temp   Perf          Pwr:Usage/Cap |         Memory-Usage | GPU-Util  Compute M. |</span><br><span class="line">|                                         |                      |               MIG M. |</span><br><span class="line">|=========================================+======================+======================|</span><br><span class="line">|   0  Tesla V100-SXM2-32GB           Off | 00000000:00:10.0 Off |                    0 |</span><br><span class="line">| N/A   56C    P0             212W / 300W |   8486MiB / 32768MiB |     84%      Default |</span><br><span class="line">|                                         |                      |                  N/A |</span><br><span class="line">+-----------------------------------------+----------------------+----------------------+</span><br><span class="line">                                                                                         </span><br><span class="line">+---------------------------------------------------------------------------------------+</span><br><span class="line">| Processes:                                                                            |</span><br><span class="line">|  GPU   GI   CI        PID   Type   Process name                            GPU Memory |</span><br><span class="line">|        ID   ID                                                             Usage      |</span><br><span class="line">|=======================================================================================|</span><br><span class="line">|    0   N/A  N/A   1191093      C   python                                     8484MiB |</span><br><span class="line">+---------------------------------------------------------------------------------------+</span><br></pre></td></tr></table></figure><p>log</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br></pre></td><td class="code"><pre><span class="line">(ai) yiyu@ubuntu:~/nfs/workspace/qwen3_08b_tunning$ python finetune_qwen.py </span><br><span class="line">🦥 Unsloth: Will patch your computer to enable 2x faster free finetuning.</span><br><span class="line">🦥 Unsloth Zoo will now patch everything to make training faster!</span><br><span class="line">============================================================</span><br><span class="line">Qwen3-8B 微调</span><br><span class="line">============================================================</span><br><span class="line"></span><br><span class="line">GPU信息:</span><br><span class="line">  CUDA可用: True</span><br><span class="line">  GPU名称: Tesla V100-SXM2-32GB</span><br><span class="line">  GPU内存: 31.7 GB</span><br><span class="line"></span><br><span class="line">1. 加载模型和分词器...</span><br><span class="line">==((====))==  Unsloth 2025.12.8: Fast Qwen3 patching. Transformers: 4.57.3.</span><br><span class="line">   \\   /|    Tesla V100-SXM2-32GB. Num GPUs = 1. Max memory: 31.739 GB. Platform: Linux.</span><br><span class="line">O^O/ \_/ \    Torch: 2.9.1+cu128. CUDA: 7.0. CUDA Toolkit: 12.8. Triton: 3.5.1</span><br><span class="line">\        /    Bfloat16 = FALSE. FA [Xformers = 0.0.33.post2. FA2 = False]</span><br><span class="line"> &quot;-____-&quot;     Free license: http://github.com/unslothai/unsloth</span><br><span class="line">Unsloth: Fast downloading is enabled - ignore downloading bars which are red colored!</span><br><span class="line">Loading checkpoint shards: 100%|█████████████████████████████████████████| 5/5 [02:30&lt;00:00, 30.01s/it]</span><br><span class="line">  ✓ 模型加载成功</span><br><span class="line">  ✓ Tokenizer设置完成</span><br><span class="line"></span><br><span class="line">2. 应用LoRA配置...</span><br><span class="line">Unsloth: Dropout = 0 is supported for fast patching. You are using dropout = 0.05.</span><br><span class="line">Unsloth will patch all other layers, except LoRA matrices, causing a performance hit.</span><br><span class="line">Unsloth 2025.12.8 patched 36 layers with 0 QKV layers, 0 O layers and 0 MLP layers.</span><br><span class="line">  ✓ LoRA应用成功 (r=16, alpha=32)</span><br><span class="line">  ✓ 可训练参数: 43,646,976 (占总参数 0.92%)</span><br><span class="line"></span><br><span class="line">3. 加载和准备数据...</span><br><span class="line">Map: 100%|█████████████████████████████████████████████████| 719/719 [00:00&lt;00:00, 11545.18 examples/s]</span><br><span class="line">  ✓ 训练数据: 719 条样本</span><br><span class="line">  ⓘ 评估数据不存在，跳过评估</span><br><span class="line"></span><br><span class="line">4. 设置训练参数...</span><br><span class="line">  ✓ 训练参数设置完成</span><br><span class="line"></span><br><span class="line">5. 创建训练器...</span><br><span class="line">Unsloth: Tokenizing [&quot;text&quot;] (num_proc=20): 100%|████████████| 719/719 [00:04&lt;00:00, 169.16 examples/s]</span><br><span class="line">  ✓ 训练器创建成功</span><br><span class="line"></span><br><span class="line">============================================================</span><br><span class="line">开始训练...</span><br><span class="line">============================================================</span><br><span class="line">The model is already on multiple devices. Skipping the move to device specified in `args`.</span><br><span class="line">==((====))==  Unsloth - 2x faster free finetuning | Num GPUs used = 1</span><br><span class="line">   \\   /|    Num examples = 719 | Num Epochs = 3 | Total steps = 270</span><br><span class="line">O^O/ \_/ \    Batch size per device = 2 | Gradient accumulation steps = 4</span><br><span class="line">\        /    Data Parallel GPUs = 1 | Total batch size (2 x 4 x 1) = 8</span><br><span class="line"> &quot;-____-&quot;     Trainable parameters = 43,646,976 of 8,234,382,336 (0.53% trained)</span><br><span class="line"><span class="meta prompt_">  0%</span><span class="language-bash">|                                                                          | 0/270 [00:00&lt;?, ?it/s]Unsloth: Will smartly offload gradients to save VRAM!</span></span><br><span class="line">&#123;&#x27;loss&#x27;: 2.2591, &#x27;grad_norm&#x27;: 0.9805265665054321, &#x27;learning_rate&#x27;: 0.0002, &#x27;epoch&#x27;: 0.11&#125;              </span><br><span class="line">&#123;&#x27;loss&#x27;: 1.6408, &#x27;grad_norm&#x27;: 0.6932195425033569, &#x27;learning_rate&#x27;: 0.0001992764570419069, &#x27;epoch&#x27;: 0.22&#125;</span><br><span class="line">&#123;&#x27;loss&#x27;: 1.5088, &#x27;grad_norm&#x27;: 0.5240709781646729, &#x27;learning_rate&#x27;: 0.00019711629845587164, &#x27;epoch&#x27;: 0.33&#125;</span><br><span class="line">&#123;&#x27;loss&#x27;: 1.411, &#x27;grad_norm&#x27;: 0.6144455671310425, &#x27;learning_rate&#x27;: 0.0001935507835925601, &#x27;epoch&#x27;: 0.44&#125;</span><br><span class="line">&#123;&#x27;loss&#x27;: 1.4867, &#x27;grad_norm&#x27;: 0.6005836129188538, &#x27;learning_rate&#x27;: 0.00018863150851539877, &#x27;epoch&#x27;: 0.56&#125;</span><br><span class="line">&#123;&#x27;loss&#x27;: 1.3918, &#x27;grad_norm&#x27;: 0.6643008589744568, &#x27;learning_rate&#x27;: 0.00018242965936120768, &#x27;epoch&#x27;: 0.67&#125;</span><br><span class="line">&#123;&#x27;loss&#x27;: 1.4473, &#x27;grad_norm&#x27;: 0.8665452003479004, &#x27;learning_rate&#x27;: 0.00017503498221564025, &#x27;epoch&#x27;: 0.78&#125;</span><br><span class="line">&#123;&#x27;loss&#x27;: 1.3993, &#x27;grad_norm&#x27;: 0.5923981666564941, &#x27;learning_rate&#x27;: 0.00016655448441021747, &#x27;epoch&#x27;: 0.89&#125;</span><br><span class="line">&#123;&#x27;loss&#x27;: 1.3726, &#x27;grad_norm&#x27;: 0.6304987072944641, &#x27;learning_rate&#x27;: 0.00015711088603430405, &#x27;epoch&#x27;: 1.0&#125;</span><br><span class="line">&#123;&#x27;loss&#x27;: 1.2395, &#x27;grad_norm&#x27;: 0.7031642198562622, &#x27;learning_rate&#x27;: 0.00014684084406997903, &#x27;epoch&#x27;: 1.11&#125;</span><br><span class="line">&#123;&#x27;loss&#x27;: 1.1672, &#x27;grad_norm&#x27;: 0.8705151081085205, &#x27;learning_rate&#x27;: 0.0001358929748480946, &#x27;epoch&#x27;: 1.22&#125;</span><br><span class="line">&#123;&#x27;loss&#x27;: 1.0753, &#x27;grad_norm&#x27;: 1.0293835401535034, &#x27;learning_rate&#x27;: 0.00012442570344228313, &#x27;epoch&#x27;: 1.33&#125;</span><br><span class="line">&#123;&#x27;loss&#x27;: 1.1042, &#x27;grad_norm&#x27;: 1.4802284240722656, &#x27;learning_rate&#x27;: 0.00011260497112202895, &#x27;epoch&#x27;: 1.44&#125;</span><br><span class="line">&#123;&#x27;loss&#x27;: 1.0878, &#x27;grad_norm&#x27;: 1.1789474487304688, &#x27;learning_rate&#x27;: 0.00010060183403992856, &#x27;epoch&#x27;: 1.56&#125;</span><br><span class="line">&#123;&#x27;loss&#x27;: 1.1023, &#x27;grad_norm&#x27;: 1.3673157691955566, &#x27;learning_rate&#x27;: 8.858998790219753e-05, &#x27;epoch&#x27;: 1.67&#125;           </span><br><span class="line">&#123;&#x27;loss&#x27;: 1.055, &#x27;grad_norm&#x27;: 1.0307848453521729, &#x27;learning_rate&#x27;: 7.674325444256899e-05, &#x27;epoch&#x27;: 1.78&#125;            </span><br><span class="line">&#123;&#x27;loss&#x27;: 1.0381, &#x27;grad_norm&#x27;: 0.9197383522987366, &#x27;learning_rate&#x27;: 6.523306607246527e-05, &#x27;epoch&#x27;: 1.89&#125;           </span><br><span class="line">&#123;&#x27;loss&#x27;: 1.037, &#x27;grad_norm&#x27;: 0.9783514142036438, &#x27;learning_rate&#x27;: 5.422598510671666e-05, &#x27;epoch&#x27;: 2.0&#125;             </span><br><span class="line">&#123;&#x27;loss&#x27;: 0.7927, &#x27;grad_norm&#x27;: 1.3467820882797241, &#x27;learning_rate&#x27;: 4.388129346376178e-05, &#x27;epoch&#x27;: 2.11&#125;           </span><br><span class="line">&#123;&#x27;loss&#x27;: 0.7877, &#x27;grad_norm&#x27;: 1.3993027210235596, &#x27;learning_rate&#x27;: 3.4348687719438665e-05, &#x27;epoch&#x27;: 2.22&#125;          </span><br><span class="line">&#123;&#x27;loss&#x27;: 0.769, &#x27;grad_norm&#x27;: 1.4056848287582397, &#x27;learning_rate&#x27;: 2.576611286891901e-05, &#x27;epoch&#x27;: 2.33&#125;            </span><br><span class="line">&#123;&#x27;loss&#x27;: 0.7288, &#x27;grad_norm&#x27;: 1.5049540996551514, &#x27;learning_rate&#x27;: 1.825776614411082e-05, &#x27;epoch&#x27;: 2.44&#125;           </span><br><span class="line">&#123;&#x27;loss&#x27;: 0.6902, &#x27;grad_norm&#x27;: 1.1971173286437988, &#x27;learning_rate&#x27;: 1.1932299773007228e-05, &#x27;epoch&#x27;: 2.56&#125;          </span><br><span class="line">&#123;&#x27;loss&#x27;: 0.7905, &#x27;grad_norm&#x27;: 1.5358942747116089, &#x27;learning_rate&#x27;: 6.881248688597553e-06, &#x27;epoch&#x27;: 2.67&#125;           </span><br><span class="line">&#123;&#x27;loss&#x27;: 0.7258, &#x27;grad_norm&#x27;: 1.0989397764205933, &#x27;learning_rate&#x27;: 3.1777059397436692e-06, &#x27;epoch&#x27;: 2.78&#125;          </span><br><span class="line">&#123;&#x27;loss&#x27;: 0.7475, &#x27;grad_norm&#x27;: 0.9426413178443909, &#x27;learning_rate&#x27;: 8.752649719641848e-07, &#x27;epoch&#x27;: 2.89&#125;           </span><br><span class="line">&#123;&#x27;loss&#x27;: 0.7717, &#x27;grad_norm&#x27;: 1.2142314910888672, &#x27;learning_rate&#x27;: 7.244084232338466e-09, &#x27;epoch&#x27;: 3.0&#125;            </span><br><span class="line">&#123;&#x27;train_runtime&#x27;: 1060.9473, &#x27;train_samples_per_second&#x27;: 2.033, &#x27;train_steps_per_second&#x27;: 0.254, &#x27;train_loss&#x27;: 1.1343570991798684, &#x27;epoch&#x27;: 3.0&#125;</span><br><span class="line"><span class="meta prompt_">100%</span><span class="language-bash">|████████████████████████████████████████████████████████████████████████████| 270/270 [17:40&lt;00:00,  3.93s/it]</span></span><br><span class="line"></span><br><span class="line">============================================================</span><br><span class="line">训练完成！</span><br><span class="line">============================================================</span><br><span class="line"></span><br><span class="line">训练统计:</span><br><span class="line">  总训练步数: 270</span><br><span class="line">  训练耗时: 1060.95 秒</span><br><span class="line">  每秒步数: 2.03</span><br><span class="line"></span><br><span class="line">6. 保存模型...</span><br><span class="line">  ✓ 模型保存到: /home/yiyu/nfs/workspace/qwen3_08b_tunning/qwen3-8b-finetuned</span><br><span class="line">  ✓ 训练参数保存</span><br><span class="line"></span><br><span class="line">============================================================</span><br><span class="line">微调流程完成！</span><br><span class="line">============================================================</span><br></pre></td></tr></table></figure><h3 id="4-3-微调后模型文件结构"><a href="#4-3-微调后模型文件结构" class="headerlink" title="4.3 微调后模型文件结构"></a>4.3 微调后模型文件结构</h3><p>训练完成后，生成的模型目录结构如下：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line">qwen3-8b-finetuned/</span><br><span class="line">├── adapter_config.json</span><br><span class="line">├── adapter_model.safetensors</span><br><span class="line">├── added_tokens.json</span><br><span class="line">├── chat_template.jinja</span><br><span class="line">├── checkpoint-100/          # 检查点目录</span><br><span class="line">│   ├── adapter_config.json</span><br><span class="line">│   ├── adapter_model.safetensors</span><br><span class="line">│   ├── optimizer.pt</span><br><span class="line">│   ├── scheduler.pt</span><br><span class="line">│   ├── trainer_state.json</span><br><span class="line">│   └── ...</span><br><span class="line">├── checkpoint-200/          # 检查点目录</span><br><span class="line">│   └── ...</span><br><span class="line">├── checkpoint-270/          # 最终检查点目录</span><br><span class="line">│   └── ...</span><br><span class="line">├── merges.txt</span><br><span class="line">├── README.md</span><br><span class="line">├── special_tokens_map.json</span><br><span class="line">├── test_model.py           # 测试脚本</span><br><span class="line">├── tokenizer_config.json</span><br><span class="line">├── tokenizer.json</span><br><span class="line">├── training_args.bin</span><br><span class="line">├── training_args.json</span><br><span class="line">└── vocab.json</span><br><span class="line"></span><br><span class="line">4 directories, 61 files</span><br></pre></td></tr></table></figure><h2 id="五、模型测试"><a href="#五、模型测试" class="headerlink" title="五、模型测试"></a>五、模型测试</h2><h3 id="5-1-测试脚本"><a href="#5-1-测试脚本" class="headerlink" title="5.1 测试脚本"></a>5.1 测试脚本</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">#!/usr/bin/env python3</span></span><br><span class="line"><span class="string">&quot;&quot;&quot;</span></span><br><span class="line"><span class="string">微调后的模型测试脚本 - 支持命令行参数输入模型路径</span></span><br><span class="line"><span class="string">&quot;&quot;&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> torch</span><br><span class="line"><span class="keyword">from</span> transformers <span class="keyword">import</span> AutoModelForCausalLM, AutoTokenizer</span><br><span class="line"><span class="keyword">import</span> sys</span><br><span class="line"><span class="keyword">import</span> os</span><br><span class="line"><span class="keyword">import</span> argparse</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">main</span>():</span><br><span class="line">    <span class="comment"># 创建命令行参数解析器</span></span><br><span class="line">    parser = argparse.ArgumentParser(description=<span class="string">&quot;测试微调后的Qwen模型&quot;</span>)</span><br><span class="line">    parser.add_argument(</span><br><span class="line">        <span class="string">&quot;--model_path&quot;</span>, </span><br><span class="line">        <span class="built_in">type</span>=<span class="built_in">str</span>, </span><br><span class="line">        default=<span class="string">&quot;/home/yiyu/nfs/workspace/qwen3_08b_tunning/Qwen3-8B&quot;</span>,</span><br><span class="line">        <span class="built_in">help</span>=<span class="string">&quot;模型路径，默认为原始Qwen3-8B模型&quot;</span></span><br><span class="line">    )</span><br><span class="line">    parser.add_argument(</span><br><span class="line">        <span class="string">&quot;--max_tokens&quot;</span>,</span><br><span class="line">        <span class="built_in">type</span>=<span class="built_in">int</span>,</span><br><span class="line">        default=<span class="number">10000</span>,</span><br><span class="line">        <span class="built_in">help</span>=<span class="string">&quot;最大生成token数，默认为10000&quot;</span></span><br><span class="line">    )</span><br><span class="line">    </span><br><span class="line">    <span class="comment"># 解析命令行参数</span></span><br><span class="line">    args = parser.parse_args()</span><br><span class="line">    </span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&quot;加载模型: <span class="subst">&#123;args.model_path&#125;</span>&quot;</span>)</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&quot;最大生成token数: <span class="subst">&#123;args.max_tokens&#125;</span>&quot;</span>)</span><br><span class="line">    </span><br><span class="line">    <span class="comment"># 检查模型路径是否存在</span></span><br><span class="line">    <span class="keyword">if</span> <span class="keyword">not</span> os.path.exists(args.model_path):</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">f&quot;错误: 模型路径不存在: <span class="subst">&#123;args.model_path&#125;</span>&quot;</span>)</span><br><span class="line">        sys.exit(<span class="number">1</span>)</span><br><span class="line">    </span><br><span class="line">    <span class="comment"># 加载tokenizer和模型</span></span><br><span class="line">    <span class="keyword">try</span>:</span><br><span class="line">        <span class="comment"># 加载tokenizer（分词器），用于文本编码和解码</span></span><br><span class="line">        tokenizer = AutoTokenizer.from_pretrained(args.model_path)</span><br><span class="line">        </span><br><span class="line">        <span class="comment"># 加载因果语言模型（用于文本生成）</span></span><br><span class="line">        model = AutoModelForCausalLM.from_pretrained(</span><br><span class="line">            args.model_path,  <span class="comment"># 模型路径</span></span><br><span class="line">            dtype=torch.float16,  <span class="comment"># 模型权重数据类型，float16可减少内存使用，使用dtype而非torch_dtype</span></span><br><span class="line">            device_map=<span class="string">&quot;auto&quot;</span>,  <span class="comment"># 自动将模型层分配到可用GPU上，支持多GPU</span></span><br><span class="line">            trust_remote_code=<span class="literal">True</span>,  <span class="comment"># 信任远程代码执行（对于某些自定义模型是必需的）</span></span><br><span class="line">        )</span><br><span class="line">    <span class="keyword">except</span> Exception <span class="keyword">as</span> e:</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">f&quot;加载模型时出错: <span class="subst">&#123;e&#125;</span>&quot;</span>)</span><br><span class="line">        sys.exit(<span class="number">1</span>)</span><br><span class="line">    </span><br><span class="line">    <span class="comment"># 测试问题</span></span><br><span class="line">    test_questions = [</span><br><span class="line">        <span class="string">&quot;3年制研究生的毕业论文从开题到答辩通过最短需要多长时间？&quot;</span></span><br><span class="line">    ]</span><br><span class="line">    </span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot;\n&quot;</span> + <span class="string">&quot;=&quot;</span> * <span class="number">60</span>)</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot;模型推理测试&quot;</span>)</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot;=&quot;</span> * <span class="number">60</span>)</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">for</span> i, question <span class="keyword">in</span> <span class="built_in">enumerate</span>(test_questions, <span class="number">1</span>):</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">f&quot;\n问题 <span class="subst">&#123;i&#125;</span>: <span class="subst">&#123;question&#125;</span>&quot;</span>)</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">&quot;-&quot;</span> * <span class="number">50</span>)</span><br><span class="line">        </span><br><span class="line">        <span class="comment"># 构建提示，使用Qwen模型的特定对话格式</span></span><br><span class="line">        prompt = <span class="string">f&quot;&lt;|im_start|&gt;user\n<span class="subst">&#123;question&#125;</span>&lt;|im_end|&gt;\n&lt;|im_start|&gt;assistant\n&quot;</span></span><br><span class="line">        </span><br><span class="line">        <span class="comment"># 编码输入文本为模型可理解的token ID</span></span><br><span class="line">        inputs = tokenizer(prompt, return_tensors=<span class="string">&quot;pt&quot;</span>).to(<span class="string">&quot;cuda&quot;</span>)</span><br><span class="line">        </span><br><span class="line">        <span class="comment"># 生成文本</span></span><br><span class="line">        <span class="keyword">try</span>:</span><br><span class="line">            <span class="keyword">with</span> torch.no_grad():  <span class="comment"># 禁用梯度计算，减少内存使用</span></span><br><span class="line">                outputs = model.generate(</span><br><span class="line">                    **inputs,  <span class="comment"># 解包输入tensors（包含input_ids, attention_mask等）</span></span><br><span class="line">                    max_new_tokens=args.max_tokens,  <span class="comment"># 最大新生成的token数量（不包括输入）</span></span><br><span class="line">                    temperature=<span class="number">0.7</span>,  <span class="comment"># 温度参数：控制随机性，较低的值使输出更确定性</span></span><br><span class="line">                    top_p=<span class="number">0.9</span>,  <span class="comment"># 核采样参数：仅考虑累积概率达到top_p的token</span></span><br><span class="line">                    do_sample=<span class="literal">True</span>,  <span class="comment"># 启用采样模式（而非贪婪解码）</span></span><br><span class="line">                    pad_token_id=tokenizer.eos_token_id,  <span class="comment"># 指定填充token ID为结束token ID</span></span><br><span class="line">                )</span><br><span class="line">        <span class="keyword">except</span> Exception <span class="keyword">as</span> e:</span><br><span class="line">            <span class="built_in">print</span>(<span class="string">f&quot;生成时出错: <span class="subst">&#123;e&#125;</span>&quot;</span>)</span><br><span class="line">            <span class="built_in">print</span>(<span class="string">&quot;可能需要减少max_tokens值，或者检查GPU内存&quot;</span>)</span><br><span class="line">            <span class="keyword">continue</span></span><br><span class="line">        </span><br><span class="line">        <span class="comment"># 解码生成的token ID为文本</span></span><br><span class="line">        response = tokenizer.decode(outputs[<span class="number">0</span>], skip_special_tokens=<span class="literal">False</span>)</span><br><span class="line">        </span><br><span class="line">        <span class="comment"># 提取助手回复（仅保留assistant之后的文本）</span></span><br><span class="line">        <span class="keyword">if</span> <span class="string">&quot;&lt;|im_start|&gt;assistant&quot;</span> <span class="keyword">in</span> response:</span><br><span class="line">            response = response.split(<span class="string">&quot;&lt;|im_start|&gt;assistant&quot;</span>)[-<span class="number">1</span>]</span><br><span class="line">        <span class="keyword">if</span> <span class="string">&quot;&lt;|im_end|&gt;&quot;</span> <span class="keyword">in</span> response:</span><br><span class="line">            response = response.split(<span class="string">&quot;&lt;|im_end|&gt;&quot;</span>)[<span class="number">0</span>]</span><br><span class="line">        </span><br><span class="line">        <span class="built_in">print</span>(<span class="string">f&quot;回答: <span class="subst">&#123;response.strip()&#125;</span>&quot;</span>)</span><br><span class="line">        </span><br><span class="line">        <span class="comment"># 统计生成的token数量</span></span><br><span class="line">        generated_tokens = outputs[<span class="number">0</span>][inputs[<span class="string">&#x27;input_ids&#x27;</span>].shape[<span class="number">1</span>]:]</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">f&quot;生成的token数: <span class="subst">&#123;<span class="built_in">len</span>(generated_tokens)&#125;</span>&quot;</span>)</span><br><span class="line">    </span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot;\n&quot;</span> + <span class="string">&quot;=&quot;</span> * <span class="number">60</span>)</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot;测试完成！&quot;</span>)</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot;=&quot;</span> * <span class="number">60</span>)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">&quot;__main__&quot;</span>:</span><br><span class="line">    main()</span><br><span class="line"></span><br></pre></td></tr></table></figure><h3 id="5-2-测试基座模型"><a href="#5-2-测试基座模型" class="headerlink" title="5.2 测试基座模型"></a>5.2 测试基座模型</h3><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br></pre></td><td class="code"><pre><span class="line">(ai) yiyu@ubuntu:~/nfs/workspace/qwen3_08b_tunning$ python test_model.py --model_path=/home/yiyu/nfs/workspace/qwen3_08b_tunning/Qwen3-8B</span><br><span class="line">加载模型: /home/yiyu/nfs/workspace/qwen3_08b_tunning/Qwen3-8B</span><br><span class="line">最大生成token数: 10000</span><br><span class="line">Loading checkpoint shards: 100%|█████████████████████████████████████████████████████| 5/5 [01:41&lt;00:00, 20.35s/it]</span><br><span class="line"></span><br><span class="line">============================================================</span><br><span class="line">模型推理测试</span><br><span class="line">============================================================</span><br><span class="line"></span><br><span class="line">问题 1: 3年制研究生的毕业论文从开题到答辩通过最短需要多长时间？</span><br><span class="line">--------------------------------------------------</span><br><span class="line">回答: &lt;think&gt;</span><br><span class="line">嗯，用户问的是3年制研究生的毕业论文从开题到答辩通过最短需要多长时间。首先，我需要确认用户的具体情况。可能用户是正在准备研究生学习的本科生，或者已经入学但对时间安排有疑问的研究生。也有可能用户是在考虑是否选择3年制研究生项目，想了解时间上的紧凑程度。</span><br><span class="line"></span><br><span class="line">接下来，我得回忆一下通常的研究生流程。一般来说，3年制的研究生项目可能包括课程学习、论文研究、开题报告、中期检查、论文撰写、预审、答辩等环节。每个环节所需时间不同，但用户问的是最短时间，所以需要考虑每个步骤是否能够尽可能快速完成，没有拖延。</span><br><span class="line"></span><br><span class="line">首先，开题报告通常在入学后的第二学期进行，但有些学校可能在第一学期就安排。如果用户想最短时间完成，可能需要在入学后尽快完成开题。不过，开题报告需要导师指导，可能需要几个月时间准备，但最短可能是一两个月。</span><br><span class="line"></span><br><span class="line">然后是论文研究和撰写。如果开题之后立即开始研究，可能需要几个月时间收集数据、分析结果。但最短时间的话，可能需要至少6个月到一年，这取决于研究的复杂性和数据获取的难易程度。</span><br><span class="line"></span><br><span class="line">中期检查通常是开题之后的一个阶段，用来评估研究进展。如果中期检查顺利通过，可能不需要额外时间。但最短情况下，可能只需要几个月。</span><br><span class="line"></span><br><span class="line">论文撰写和修改可能需要几个月时间，加上预审和答辩。如果所有步骤都顺利，可能需要几个月。答辩通常在论文完成后进行，可能需要一两个月的时间准备。</span><br><span class="line"></span><br><span class="line">不过，用户问的是最短时间，所以需要考虑每个步骤是否都能压缩到最短。例如，开题报告可能只需要几周，研究和撰写可能需要几个月，中期检查可能在开题后几个月内完成，论文修改和预审可能需要几周，答辩可能在几个月后。</span><br><span class="line"></span><br><span class="line">不过，实际操作中，每个步骤都需要时间，尤其是研究和撰写部分，可能无法压缩太多。另外，不同学校和导师的要求可能不同，有些学校可能有严格的时间安排，而有些可能更灵活。</span><br><span class="line"></span><br><span class="line">还需要考虑学生个人的能力和效率。如果学生非常高效，可能可以缩短时间，但通常来说，最短时间可能在1年左右，但可能需要更长时间。例如，开题到答辩可能需要至少1年，但实际可能需要更长，比如1.5年或更久。</span><br><span class="line"></span><br><span class="line">另外，用户可能想知道是否有可能在更短时间内完成，比如半年，但这种情况非常罕见，除非研究内容非常简单，或者学生有大量资源支持，但通常不现实。</span><br><span class="line"></span><br><span class="line">需要提醒用户，最短时间可能因学校、导师、研究课题等因素而异，建议咨询所在学校的具体要求，并合理规划时间。同时，要确保研究质量，不能为了赶时间而牺牲论文质量。</span><br><span class="line">&lt;/think&gt;</span><br><span class="line"></span><br><span class="line">3年制研究生的毕业论文从开题到答辩通过的最短时间，通常取决于多个因素，包括学校要求、导师指导、研究课题的复杂性、学生的效率等。以下是基于一般情况的分析和时间线参考：</span><br><span class="line"></span><br><span class="line">---</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_">#</span><span class="language-bash"><span class="comment">## **最短时间线（理想情况）**</span></span></span><br><span class="line">假设所有环节均高效推进，且无重大延误，**最短可能需要约1年**（约12个月）。具体时间线如下：</span><br><span class="line"></span><br><span class="line">1. **开题报告（1-2个月）**  </span><br><span class="line">   - 入学后第1-2学期完成，需与导师讨论研究方向、确定题目、制定研究计划。</span><br><span class="line"></span><br><span class="line">2. **论文研究与撰写（6-12个月）**  </span><br><span class="line">   - 研究阶段（数据收集、实验、分析）：3-6个月  </span><br><span class="line">   - 论文撰写：3-6个月（需反复修改）  </span><br><span class="line">   - 若研究内容简单（如纯理论分析、文献综述），可能缩短至3-4个月。</span><br><span class="line"></span><br><span class="line">3. **中期检查（1-2个月）**  </span><br><span class="line">   - 评估研究进展，通常在开题后6-12个月内进行。</span><br><span class="line"></span><br><span class="line">4. **论文预审与修改（1-2个月）**  </span><br><span class="line">   - 根据导师反馈调整论文，准备答辩材料。</span><br><span class="line"></span><br><span class="line">5. **答辩（1个月）**  </span><br><span class="line">   - 答辩准备（1-2周） + 正式答辩（1周）。</span><br><span class="line"></span><br><span class="line">**总时间**：开题后约1年（具体可能为10-12个月）。</span><br><span class="line"></span><br><span class="line">---</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_">#</span><span class="language-bash"><span class="comment">## **实际常见时间线**</span></span></span><br><span class="line">在实际情况中，由于研究复杂性、数据获取难度、导师反馈周期等因素，**最短可能需要1.5-2年**。例如：</span><br><span class="line"></span><br><span class="line">- **开题后1.5年**：研究阶段耗时更长，或需多次修改。</span><br><span class="line">- **开题后2年**：若课题涉及实验、实地调查或复杂数据分析，时间可能进一步延长。</span><br><span class="line"></span><br><span class="line">---</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_">#</span><span class="language-bash"><span class="comment">## **关键影响因素**</span></span></span><br><span class="line">1. **研究课题性质**  </span><br><span class="line">   - 理论研究（如纯文献分析）：时间较短。  </span><br><span class="line">   - 实验/实证研究：需更多时间收集数据、分析结果。</span><br><span class="line"></span><br><span class="line">2. **导师与学校要求**  </span><br><span class="line">   - 部分学校要求中期检查、预审等环节，可能延长周期。</span><br><span class="line"></span><br><span class="line">3. **学生效率与资源**  </span><br><span class="line">   - 高效的学生可能压缩时间，但需确保质量。</span><br><span class="line"></span><br><span class="line">4. **答辩通过率**  </span><br><span class="line">   - 若答辩未通过，需补充修改，可能额外耗时。</span><br><span class="line"></span><br><span class="line">---</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_">#</span><span class="language-bash"><span class="comment">## **注意事项**</span></span></span><br><span class="line">- **不可压缩的环节**：研究本身是核心，需保证质量，不能为赶时间牺牲成果。  </span><br><span class="line">- **合理规划**：建议在开题时与导师明确时间节点，预留缓冲时间。  </span><br><span class="line">- **学校差异**：不同高校对毕业论文的要求不同，需以所在学校的具体规定为准。</span><br><span class="line"></span><br><span class="line">---</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_">#</span><span class="language-bash"><span class="comment">## **结论**</span></span></span><br><span class="line">在理想情况下，**3年制研究生从开题到答辩通过的最短时间约为1年**，但实际中可能需要1.5-2年。建议学生提前规划，与导师保持沟通，确保研究进度与质量平衡。</span><br><span class="line">生成的token数: 1281</span><br><span class="line"></span><br><span class="line">============================================================</span><br><span class="line">测试完成！</span><br><span class="line">============================================================</span><br></pre></td></tr></table></figure><h3 id="5-3-测试微调模型"><a href="#5-3-测试微调模型" class="headerlink" title="5.3 测试微调模型"></a>5.3 测试微调模型</h3><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line">(ai) yiyu@ubuntu:~/nfs/workspace/qwen3_08b_tunning$ python test_model.py --model_path=/home/yiyu/nfs/workspace/qwen3_08b_tunning/qwen3-8b-finetuned</span><br><span class="line">加载模型: /home/yiyu/nfs/workspace/qwen3_08b_tunning/qwen3-8b-finetuned</span><br><span class="line">最大生成token数: 10000</span><br><span class="line">Loading checkpoint shards: 100%|████████████████████████████████████████████████████████| 5/5 [01:40&lt;00:00, 20.09s/it]</span><br><span class="line"></span><br><span class="line">============================================================</span><br><span class="line">模型推理测试</span><br><span class="line">============================================================</span><br><span class="line"></span><br><span class="line">问题 1: 3年制研究生的毕业论文从开题到答辩通过最短需要多长时间？</span><br><span class="line">--------------------------------------------------</span><br><span class="line">回答: 3年制研究生的毕业论文从开题到答辩通过最短需要1年时间，即开题时间为入学后的第2学期，最迟答辩时间为入学后的第6学期。</span><br><span class="line">生成的token数: 41</span><br><span class="line"></span><br><span class="line">============================================================</span><br><span class="line">测试完成！</span><br><span class="line">============================================================</span><br></pre></td></tr></table></figure><h3 id="5-4-参考答案"><a href="#5-4-参考答案" class="headerlink" title="5.4 参考答案"></a>5.4 参考答案</h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">&#123;&quot;conversations&quot;:[&#123;&quot;role&quot;:&quot;user&quot;,&quot;content&quot;:&quot;三年制硕士生从开题通过到申请学位论文答辩的最短时间要求是多少？&quot;&#125;,&#123;&quot;role&quot;:&quot;assistant&quot;,&quot;content&quot;:&quot;三年制硕士生从开题答辩通过到申请学位论文答辩的最短时间要求是不得少于12个月。这一规定明确规定了开题通过后必须经过至少一年的时间才能申请论文答辩。&quot;&#125;]&#125;</span><br></pre></td></tr></table></figure><h2 id="六、总结"><a href="#六、总结" class="headerlink" title="六、总结"></a>六、总结</h2><p>从测试结果可以看出：</p><p>微调前（基座模型）：</p><ul><li>回答详尽但冗余，生成1281个tokens</li><li>包含大量通用知识和推理过程</li></ul><p>微调后：</p><ul><li>回答简洁精准，仅41个tokens</li><li>直接命中问题核心，给出明确的时间要求</li><li>准确反映了数据集中”不得少于12个月”的规定</li></ul><p>本次实践验证了：</p><ul><li>轻量级微调在特定领域知识注入上的有效性</li><li>开源模型+领域数据是企业构建专业AI服务的可行路径</li><li>LoRA技术降低了模型个性化定制门槛</li></ul><p>本次Qwen3-8B微调实践成功展示了：</p><ul><li><strong>技术可行性</strong>：在有限资源下完成大模型领域适应</li><li><strong>效果显著性</strong>：微调后模型在特定领域回答质量显著提升</li><li><strong>流程标准化</strong>：建立了可复现的微调工作流程</li></ul><p>通过”预训练大模型+LoRA微调+领域数据”的技术路线，企业可以经济高效地构建具备专业能力的AI系统，为垂直领域的智能化应用提供了实践参考。</p>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;Qwen3-微调实践笔记&quot;&gt;&lt;a href=&quot;#Qwen3-微调实践笔记&quot; class=&quot;headerlink&quot; title=&quot;Qwen3 微调实践笔记&quot;&gt;&lt;/a&gt;Qwen3 微调实践笔记&lt;/h1&gt;&lt;h2 id=&quot;一、模型微调的意义&quot;&gt;&lt;a href=&quot;#一、模型</summary>
      
    
    
    
    <category term="ai" scheme="https://www.helloyiyu.com/categories/ai/"/>
    
    
    <category term="python" scheme="https://www.helloyiyu.com/tags/python/"/>
    
    <category term="ai" scheme="https://www.helloyiyu.com/tags/ai/"/>
    
    <category term="数据集" scheme="https://www.helloyiyu.com/tags/%E6%95%B0%E6%8D%AE%E9%9B%86/"/>
    
    <category term="qwen3" scheme="https://www.helloyiyu.com/tags/qwen3/"/>
    
    <category term="lora" scheme="https://www.helloyiyu.com/tags/lora/"/>
    
    <category term="Unsloth" scheme="https://www.helloyiyu.com/tags/Unsloth/"/>
    
  </entry>
  
  <entry>
    <title>python调用系统摄像头拍照</title>
    <link href="https://www.helloyiyu.com/332941172.html"/>
    <id>https://www.helloyiyu.com/332941172.html</id>
    <published>2025-11-24T04:19:24.000Z</published>
    <updated>2025-11-24T12:59:42.000Z</updated>
    
    <content type="html"><![CDATA[<p>为了方便准备审校数据集，开发拍照工具，将之前手写修改的审校内容拍摄成图片，提供给后续流程来产生数据集。</p><pre><code class="language-python"># coding:utf-8import cv2import timeimport os# 打开摄像头，参数为0时调用本地摄像头，参数为1时调用外接摄像头cap = cv2.VideoCapture(0, cv2.CAP_DSHOW)# 设置保存的分辨率save_width = 2480  # 3508 # 宽度save_height = 3508 # 2480 # 高度cap.set(cv2.CAP_PROP_FRAME_WIDTH, save_width)cap.set(cv2.CAP_PROP_FRAME_HEIGHT, save_height)# 设置显示的分辨率display_width = 1000 # 宽度display_height = 600 # 高度# 创建保存目录save_dir = &quot;D:/yiyu/photo/&quot;if not os.path.exists(save_dir):    os.makedirs(save_dir)# 检查摄像头是否成功打开if not cap.isOpened():    print(&quot;Error: Could not open camera&quot;)    exit()while True:    ret, frame = cap.read()    if not ret:        print(&quot;Error: Failed to capture frame&quot;)        break            # 调整显示帧的大小    display_frame = cv2.resize(frame, (display_width, display_height))    cv2.imshow(&quot;Capture_Paizhao&quot;, display_frame)        k = cv2.waitKey(1) &amp; 0xFF    if k == ord(&#39;s&#39;): # 按下s键，进入下面的保存图片操作        current_time = time.time()        milliseconds = int(round(current_time * 1000))        filename = save_dir + str(milliseconds) + &quot;.jpg&quot;        # 旋转图像90度逆时针        rotated_frame = cv2.rotate(frame, cv2.ROTATE_90_COUNTERCLOCKWISE)        success = cv2.imwrite(filename, rotated_frame)        if success:            print(&quot;save &quot; + str(milliseconds) + &quot;.jpg successfully!&quot;)        else:            print(&quot;Error: Failed to save image&quot;)        print(&quot;-------------------------&quot;)    elif k == ord(&#39;q&#39;): # 按下q键，程序退出        break# 释放摄像头和销毁窗口cap.release()cv2.destroyAllWindows()</code></pre>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;为了方便准备审校数据集，开发拍照工具，将之前手写修改的审校内容拍摄成图片，提供给后续流程来产生数据集。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;# coding:utf-8
import cv2
import time
import o</summary>
      
    
    
    
    <category term="python" scheme="https://www.helloyiyu.com/categories/python/"/>
    
    <category term="ai" scheme="https://www.helloyiyu.com/categories/python/ai/"/>
    
    
    <category term="python" scheme="https://www.helloyiyu.com/tags/python/"/>
    
    <category term="ai" scheme="https://www.helloyiyu.com/tags/ai/"/>
    
    <category term="数据集" scheme="https://www.helloyiyu.com/tags/%E6%95%B0%E6%8D%AE%E9%9B%86/"/>
    
  </entry>
  
  <entry>
    <title>图片审校区域标记工具</title>
    <link href="https://www.helloyiyu.com/3045380748.html"/>
    <id>https://www.helloyiyu.com/3045380748.html</id>
    <published>2025-11-21T02:18:09.000Z</published>
    <updated>2025-11-24T14:14:55.000Z</updated>
    
    <content type="html"><![CDATA[<p>为了方便准备审校数据集，开发标记工具，将存在审校修改的图片区域单独保存，提供给后续流程来产生数据集。</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> nicegui <span class="keyword">import</span> ui</span><br><span class="line"><span class="keyword">import</span> os</span><br><span class="line"><span class="keyword">import</span> glob</span><br><span class="line"><span class="keyword">import</span> time</span><br><span class="line"><span class="keyword">from</span> PIL <span class="keyword">import</span> Image</span><br><span class="line"></span><br><span class="line"><span class="comment"># 自动管理图片路径和保存目录</span></span><br><span class="line">INPUT_DIR = <span class="string">&#x27;static/input&#x27;</span></span><br><span class="line">DONE_DIR = <span class="string">&#x27;static/done&#x27;</span></span><br><span class="line">CROPS_DIR = <span class="string">&#x27;static/crops&#x27;</span></span><br><span class="line">GRAY_IMAGE = <span class="string">&#x27;static/gray.png&#x27;</span></span><br><span class="line"></span><br><span class="line">os.makedirs(INPUT_DIR, exist_ok=<span class="literal">True</span>)</span><br><span class="line">os.makedirs(DONE_DIR, exist_ok=<span class="literal">True</span>)</span><br><span class="line">os.makedirs(CROPS_DIR, exist_ok=<span class="literal">True</span>)</span><br><span class="line"></span><br><span class="line">DISPLAY_WIDTH = <span class="number">1240</span></span><br><span class="line">DISPLAY_HEIGHT = <span class="number">1790</span></span><br><span class="line">SELECT_BOX_HEIGHT = <span class="number">300</span></span><br><span class="line"></span><br><span class="line">select_box_y = <span class="number">0</span></span><br><span class="line">img = <span class="literal">None</span></span><br><span class="line">select_box = <span class="literal">None</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">ensure_gray_image</span>():</span><br><span class="line">    <span class="keyword">if</span> <span class="keyword">not</span> os.path.exists(GRAY_IMAGE):</span><br><span class="line">        img_gray = Image.new(<span class="string">&#x27;RGB&#x27;</span>, (DISPLAY_WIDTH, DISPLAY_HEIGHT), (<span class="number">200</span>, <span class="number">200</span>, <span class="number">200</span>))</span><br><span class="line">        img_gray.save(GRAY_IMAGE)</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">get_input_images</span>():</span><br><span class="line">    <span class="keyword">return</span> <span class="built_in">sorted</span>(glob.glob(os.path.join(INPUT_DIR, <span class="string">&#x27;*.jpg&#x27;</span>)) + glob.glob(os.path.join(INPUT_DIR, <span class="string">&#x27;*.png&#x27;</span>)))</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">get_first_image</span>():</span><br><span class="line">    images = get_input_images()</span><br><span class="line">    <span class="keyword">if</span> images:</span><br><span class="line">        <span class="keyword">return</span> images[<span class="number">0</span>]</span><br><span class="line">    ensure_gray_image()</span><br><span class="line">    <span class="keyword">return</span> GRAY_IMAGE</span><br><span class="line"></span><br><span class="line">current_image_path = get_first_image()</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">update_select_box</span>(<span class="params">event</span>):</span><br><span class="line">    <span class="keyword">global</span> select_box_y</span><br><span class="line">    <span class="keyword">if</span> event <span class="keyword">is</span> <span class="literal">None</span>:</span><br><span class="line">        y = select_box_y</span><br><span class="line">    <span class="keyword">else</span>:</span><br><span class="line">        offset_y = <span class="built_in">getattr</span>(event.args, <span class="string">&#x27;offsetY&#x27;</span>, <span class="literal">None</span>)</span><br><span class="line">        <span class="keyword">if</span> offset_y <span class="keyword">is</span> <span class="literal">None</span>:</span><br><span class="line">            offset_y = event.args.get(<span class="string">&#x27;offsetY&#x27;</span>, <span class="number">0</span>) <span class="keyword">if</span> <span class="built_in">isinstance</span>(event.args, <span class="built_in">dict</span>) <span class="keyword">else</span> <span class="number">0</span></span><br><span class="line">        y = <span class="built_in">max</span>(<span class="number">0</span>, <span class="built_in">min</span>(offset_y, DISPLAY_HEIGHT - SELECT_BOX_HEIGHT))</span><br><span class="line">    select_box_y = y</span><br><span class="line">    select_box.style(<span class="string">f&#x27;top: <span class="subst">&#123;select_box_y&#125;</span>px; left: 0px; width: <span class="subst">&#123;DISPLAY_WIDTH&#125;</span>px; height: <span class="subst">&#123;SELECT_BOX_HEIGHT&#125;</span>px; position: absolute; border: 2px solid #00f; background: rgba(0,0,255,0.1); pointer-events: none;&#x27;</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">crop_and_save</span>(<span class="params">event</span>):</span><br><span class="line">    <span class="keyword">global</span> select_box_y, current_image_path</span><br><span class="line">    img_path = current_image_path</span><br><span class="line">    <span class="keyword">if</span> <span class="keyword">not</span> os.path.exists(img_path):</span><br><span class="line">        ui.notify(<span class="string">&#x27;图片不存在&#x27;</span>)</span><br><span class="line">        <span class="keyword">return</span></span><br><span class="line">    <span class="keyword">try</span>:</span><br><span class="line">        img_obj = Image.<span class="built_in">open</span>(img_path)</span><br><span class="line">        img_width, img_height = img_obj.size</span><br><span class="line">        scale = img_height / DISPLAY_HEIGHT</span><br><span class="line">        crop_left = <span class="number">0</span></span><br><span class="line">        crop_upper = <span class="built_in">int</span>(select_box_y * scale)</span><br><span class="line">        crop_right = img_width</span><br><span class="line">        crop_lower = <span class="built_in">int</span>(<span class="built_in">min</span>((select_box_y + SELECT_BOX_HEIGHT) * scale, img_height))</span><br><span class="line">        crop_box = (crop_left, crop_upper, crop_right, crop_lower)</span><br><span class="line">        crop_img = img_obj.crop(crop_box)</span><br><span class="line">        <span class="keyword">if</span> crop_img.mode == <span class="string">&quot;RGBA&quot;</span>:</span><br><span class="line">            crop_img = crop_img.convert(<span class="string">&quot;RGB&quot;</span>)</span><br><span class="line">        ts = <span class="built_in">int</span>(time.time() * <span class="number">1000</span>)</span><br><span class="line">        save_path = os.path.join(CROPS_DIR, <span class="string">f&#x27;<span class="subst">&#123;ts&#125;</span>.jpg&#x27;</span>)</span><br><span class="line">        crop_img.save(save_path)</span><br><span class="line">        ui.notify(<span class="string">f&#x27;已保存: <span class="subst">&#123;save_path&#125;</span>&#x27;</span>)</span><br><span class="line">    <span class="keyword">except</span> Exception <span class="keyword">as</span> e:</span><br><span class="line">        ui.notify(<span class="string">f&#x27;裁剪失败: <span class="subst">&#123;e&#125;</span>&#x27;</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">handle_mouse_click</span>(<span class="params">event</span>):</span><br><span class="line">    <span class="keyword">global</span> SELECT_BOX_HEIGHT</span><br><span class="line">    button = event.args.get(<span class="string">&#x27;button&#x27;</span>, <span class="number">0</span>)</span><br><span class="line">    <span class="keyword">if</span> button == <span class="number">0</span>:  <span class="comment"># left</span></span><br><span class="line">    <span class="comment">#     SELECT_BOX_HEIGHT = max(10, SELECT_BOX_HEIGHT - 10)</span></span><br><span class="line">    <span class="comment">#     update_select_box(event)</span></span><br><span class="line">    <span class="comment"># elif button == 2:  # right</span></span><br><span class="line">    <span class="comment">#     SELECT_BOX_HEIGHT += 10</span></span><br><span class="line">    <span class="comment">#     update_select_box(event)</span></span><br><span class="line">    <span class="comment"># elif button == 1:  # middle</span></span><br><span class="line">        crop_and_save(event)</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">done_and_next</span>():</span><br><span class="line">    <span class="keyword">global</span> current_image_path, img</span><br><span class="line">    images = get_input_images()</span><br><span class="line">    <span class="keyword">if</span> current_image_path <span class="keyword">in</span> images:</span><br><span class="line">        basename = os.path.basename(current_image_path)</span><br><span class="line">        <span class="comment"># 检查done目录是否已存在同名文件，若存在则加时间戳</span></span><br><span class="line">        done_path = os.path.join(DONE_DIR, basename)</span><br><span class="line">        <span class="keyword">if</span> os.path.exists(done_path):</span><br><span class="line">            name, ext = os.path.splitext(basename)</span><br><span class="line">            ts = <span class="built_in">int</span>(time.time() * <span class="number">1000</span>)</span><br><span class="line">            done_path = os.path.join(DONE_DIR, <span class="string">f&quot;<span class="subst">&#123;name&#125;</span>_<span class="subst">&#123;ts&#125;</span><span class="subst">&#123;ext&#125;</span>&quot;</span>)</span><br><span class="line">        os.rename(current_image_path, done_path)</span><br><span class="line">        ui.notify(<span class="string">f&#x27;已移到: <span class="subst">&#123;done_path&#125;</span>&#x27;</span>)</span><br><span class="line">    images = get_input_images()</span><br><span class="line">    <span class="keyword">if</span> images:</span><br><span class="line">        current_image_path = images[<span class="number">0</span>]</span><br><span class="line">    <span class="keyword">else</span>:</span><br><span class="line">        ensure_gray_image()</span><br><span class="line">        current_image_path = GRAY_IMAGE</span><br><span class="line">    img.set_source(current_image_path)</span><br><span class="line">    </span><br><span class="line"><span class="keyword">def</span> <span class="title function_">adjust_height</span>(<span class="params">delta</span>):</span><br><span class="line">    <span class="keyword">global</span> SELECT_BOX_HEIGHT</span><br><span class="line">    <span class="keyword">if</span> delta &lt; <span class="number">0</span>:</span><br><span class="line">        SELECT_BOX_HEIGHT = <span class="built_in">max</span>(<span class="number">10</span>, SELECT_BOX_HEIGHT + delta)</span><br><span class="line">    <span class="keyword">else</span>:</span><br><span class="line">        SELECT_BOX_HEIGHT += delta</span><br><span class="line">    update_select_box(<span class="literal">None</span>)</span><br><span class="line"></span><br><span class="line"><span class="meta">@ui.page(<span class="params"><span class="string">&#x27;/&#x27;</span></span>)</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">main_page</span>():</span><br><span class="line">    <span class="keyword">global</span> img, select_box, current_image_path</span><br><span class="line">    <span class="keyword">with</span> ui.row().classes(<span class="string">&#x27;w-full h-full justify-center items-center&#x27;</span>):</span><br><span class="line">        <span class="keyword">with</span> ui.column().classes(<span class="string">&#x27;w-full h-full items-center&#x27;</span>):</span><br><span class="line">            ui.label(<span class="string">&#x27;审校区域标记工具&#x27;</span>).classes(<span class="string">&#x27;text-2xl q-mb-md&#x27;</span>)</span><br><span class="line">            ui.label(<span class="string">&#x27;选择有审校修改的区域进行标记, 一页内容标记完成后点击“完成并下一张”按钮, 当图片显示为灰色时表示已经处理完&#x27;</span>)</span><br><span class="line">            <span class="keyword">with</span> ui.element(<span class="string">&#x27;div&#x27;</span>).style(<span class="string">f&#x27;position: relative; width: <span class="subst">&#123;DISPLAY_WIDTH&#125;</span>px; height: <span class="subst">&#123;DISPLAY_HEIGHT&#125;</span>px;&#x27;</span>):</span><br><span class="line">                img = ui.image(current_image_path).style(<span class="string">f&#x27;width: <span class="subst">&#123;DISPLAY_WIDTH&#125;</span>px; height: <span class="subst">&#123;DISPLAY_HEIGHT&#125;</span>px; display: block; margin-left: auto; margin-right: auto;&#x27;</span>)</span><br><span class="line">                select_box = ui.element(<span class="string">&#x27;div&#x27;</span>).style(<span class="string">f&#x27;position: absolute; top: 0px; left: 0px; width: <span class="subst">&#123;DISPLAY_WIDTH&#125;</span>px; height: <span class="subst">&#123;SELECT_BOX_HEIGHT&#125;</span>px; border: 2px solid #00f; background: rgba(0,0,255,0.1); pointer-events: none;&#x27;</span>)</span><br><span class="line">                ui.button(<span class="string">&#x27;-&#x27;</span>, on_click=<span class="keyword">lambda</span>: adjust_height(-<span class="number">10</span>)).style(<span class="string">&#x27;position: fixed; top: 10px; right: 10px; width: 30px; height: 30px;&#x27;</span>)</span><br><span class="line">                ui.button(<span class="string">&#x27;+&#x27;</span>, on_click=<span class="keyword">lambda</span>: adjust_height(<span class="number">10</span>)).style(<span class="string">&#x27;position: fixed; top: 10px; right: 50px; width: 30px; height: 30px;&#x27;</span>)</span><br><span class="line">                img.on(<span class="string">&#x27;mousemove&#x27;</span>, update_select_box)</span><br><span class="line">                img.on(<span class="string">&#x27;mousemove&#x27;</span>, update_select_box)</span><br><span class="line">                <span class="keyword">if</span> current_image_path != GRAY_IMAGE:</span><br><span class="line">                    img.on(<span class="string">&#x27;mousedown&#x27;</span>, handle_mouse_click)</span><br><span class="line">            <span class="comment"># 添加一个与图片等宽、高度为100的按钮，点击后执行done_and_next</span></span><br><span class="line">            ui.button(<span class="string">&#x27;完成并下一张&#x27;</span>, on_click=done_and_next).style(<span class="string">f&#x27;width: <span class="subst">&#123;DISPLAY_WIDTH&#125;</span>px; height: 100px; font-size: 2rem; margin-top: 10px; margin-left: auto; margin-right: auto; display: block;&#x27;</span>)</span><br><span class="line"></span><br><span class="line">ui.run(title=<span class="string">&#x27;图片裁剪工具&#x27;</span>, native=<span class="literal">False</span>, port=<span class="number">8080</span>)</span><br></pre></td></tr></table></figure>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;为了方便准备审校数据集，开发标记工具，将存在审校修改的图片区域单独保存，提供给后续流程来产生数据集。&lt;/p&gt;
&lt;figure class=&quot;highlight python&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;l</summary>
      
    
    
    
    <category term="python" scheme="https://www.helloyiyu.com/categories/python/"/>
    
    <category term="ai" scheme="https://www.helloyiyu.com/categories/python/ai/"/>
    
    
    <category term="python" scheme="https://www.helloyiyu.com/tags/python/"/>
    
    <category term="ai" scheme="https://www.helloyiyu.com/tags/ai/"/>
    
    <category term="数据集" scheme="https://www.helloyiyu.com/tags/%E6%95%B0%E6%8D%AE%E9%9B%86/"/>
    
  </entry>
  
  <entry>
    <title>工作站与博客环境</title>
    <link href="https://www.helloyiyu.com/4209178176.html"/>
    <id>https://www.helloyiyu.com/4209178176.html</id>
    <published>2025-11-21T00:20:36.000Z</published>
    <updated>2025-11-24T12:59:42.000Z</updated>
    
    <content type="html"><![CDATA[<blockquote><p>本文由 AI 根据我在服务器上的实际操作记录和三份自动化脚本整理生成，主要用于之后再次部署时参考。<br>请务必在使用前自行检查命令、版本号和配置是否符合当前环境，避免因系统更新或个人环境差异导致问题。</p></blockquote><h2 id="一、整体目标与脚本概览"><a href="#一、整体目标与脚本概览" class="headerlink" title="一、整体目标与脚本概览"></a>一、整体目标与脚本概览</h2><p>在一台 Ubuntu 24.04.3 LTS 服务器上，我为自己常用的几个场景写了三份自动化脚本，用来快速完成以下工作：</p><ul><li>GPU 环境：<ul><li>禁用 <code>nouveau</code></li><li>安装并锁定 Nvidia 驱动</li><li>安装 CUDA &#x2F; <code>nvidia-cuda-toolkit</code></li><li>安装并配置 <code>nvidia-container-toolkit</code>，让 Docker 能使用 GPU</li></ul></li><li>AI 服务：<ul><li>使用 Docker 运行 GPU 版 Ollama，可选自动拉取 Qwen3 系列模型</li></ul></li><li>博客环境：<ul><li>安装 <code>nvm</code> 和 Node LTS</li><li>安装 Hexo</li><li>初始化和部署 Hexo 博客</li></ul></li></ul><p>对应的三个脚本分别是：</p><ol><li><p><code>install_nvidia_cuda_container.sh</code><br>禁用 nouveau、锁定当前内核、自动安装推荐的 Nvidia 驱动、安装 CUDA 工具链和 <code>nvidia-container-toolkit</code>，并完成基础测试。</p></li><li><p><code>run_ollama.sh</code><br>使用 Docker 运行 GPU 版 Ollama 容器，并提供交互式选项，一键拉取常用的 Qwen3 系列模型。</p></li><li><p><code>setup_hexo_blog.sh</code><br>安装 <code>nvm</code> + Node LTS + <code>hexo-cli</code>，创建或重建 Hexo 博客目录，安装依赖并配置 Git 部署。</p></li></ol><p>下面分别介绍这三个脚本的设计思路、关键步骤和使用方式。</p><hr><h2 id="二、GPU-环境脚本：install-nvidia-cuda-container-sh"><a href="#二、GPU-环境脚本：install-nvidia-cuda-container-sh" class="headerlink" title="二、GPU 环境脚本：install_nvidia_cuda_container.sh"></a>二、GPU 环境脚本：install_nvidia_cuda_container.sh</h2><h3 id="2-1-设计目标"><a href="#2-1-设计目标" class="headerlink" title="2.1 设计目标"></a>2.1 设计目标</h3><p>这份脚本的目标是将「从零准备 GPU 环境」的一整套操作自动化，主要包括：</p><ul><li>禁用开源驱动 <code>nouveau</code>，避免与官方 Nvidia 驱动冲突；</li><li>不再强制安装新内核，而是：<ul><li>识别当前正在使用的内核版本；</li><li>尝试锁定与该版本相关的内核包，降低未来升级导致驱动失配的概率；</li></ul></li><li>使用 <code>ubuntu-drivers</code> 自动检测并安装「推荐」的 Nvidia 驱动，而不是写死某个版本（比如 <code>nvidia-driver-535</code>）；</li><li>安装 <code>nvidia-cuda-toolkit</code>，提供基础 CUDA 工具链（含 <code>nvcc</code>）；</li><li>安装并配置 <code>nvidia-container-toolkit</code>，让 Docker 容器可以访问 GPU；</li><li>最后进行三项测试：<ul><li>宿主机上的 <code>nvidia-smi</code>；</li><li>宿主机上的 <code>nvcc --version</code>；</li><li>Docker 容器内的 <code>nvidia-smi</code>。</li></ul></li></ul><p>这些步骤对应了我在手动操作时的主要流程，但用更通用、更安全的方式实现（比如用 <code>ubuntu-drivers devices</code> 自动选驱动）。</p><hr><h3 id="2-2-关键步骤拆解"><a href="#2-2-关键步骤拆解" class="headerlink" title="2.2 关键步骤拆解"></a>2.2 关键步骤拆解</h3><p>脚本的主要逻辑如下。</p><ol><li>禁用 nouveau 并更新 initramfs</li></ol><ul><li><p>创建或覆盖 <code>/etc/modprobe.d/blacklist-nouveau.conf</code>；</p></li><li><p>写入：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">blacklist nouveau</span><br><span class="line">options nouveau modeset=0</span><br></pre></td></tr></table></figure></li><li><p>执行 <code>sudo update-initramfs -u</code>，让配置在下次启动时生效。</p></li></ul><p>这样可以避免系统加载开源的 <code>nouveau</code> 模块，给后续官方驱动提供干净的环境。</p><ol start="2"><li>安装基础编译工具</li></ol><ul><li>执行 <code>sudo apt-get update</code>；</li><li>安装 <code>build-essential</code>（包含 gcc &#x2F; g++ 等），为部分驱动或编译场景做准备。</li></ul><ol start="3"><li>确认并锁定当前内核版本（不安装新内核）</li></ol><ul><li>使用 <code>uname -r</code> 读取当前正在运行的内核版本，例如 <code>6.8.0-87-generic</code>；</li><li>对以下可能存在的包进行检测并加 hold（如果存在就 <code>apt-mark hold</code>）：<ul><li><code>linux-image-&lt;当前内核版本&gt;</code></li><li><code>linux-headers-&lt;当前内核版本&gt;</code></li><li><code>linux-modules-extra-&lt;当前内核版本&gt;</code></li></ul></li></ul><p>这样做的目的是：尽量保持「当前内核 + 当前驱动」这一组合的稳定，不追求自动升级；未来如果需要升级内核，再手动解除 hold 并重新规划。</p><ol start="4"><li>自动检测并安装推荐的 Nvidia 驱动</li></ol><ul><li>如果系统已有 <code>ubuntu-drivers</code>：<ul><li>执行 <code>ubuntu-drivers devices</code>，打印可用驱动；</li><li>用 <code>awk</code> 从输出中解析带 <code>recommended</code> 标记的驱动包名（例如 <code>nvidia-driver-535</code>）；</li><li>如果解析成功，则自动执行 <code>sudo apt-get install -y &lt;推荐驱动包&gt;</code>。</li></ul></li><li>如果系统没有安装 <code>ubuntu-drivers</code>：<ul><li>先安装 <code>ubuntu-drivers-common</code>；</li><li>再重复上述检测逻辑。</li></ul></li><li>如果始终解析不到推荐驱动：<ul><li>脚本只会给出提示，让你之后手动执行 <code>ubuntu-drivers devices</code> 并根据输出自行选择驱动安装。</li></ul></li></ul><p>通过这种方式，脚本不写死驱动版本，而是配合 Ubuntu 官方的推荐策略，使脚本更适合未来系统的升级。</p><ol start="5"><li>安装 CUDA 工具链（nvidia-cuda-toolkit）</li></ol><ul><li><p>直接通过 apt 安装：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> apt-get install -y nvidia-cuda-toolkit</span><br></pre></td></tr></table></figure></li><li><p>然后尝试对常见的 CUDA 工具包名执行 <code>apt-mark hold</code>：</p><ul><li><code>cuda-toolkit-12-0</code></li><li><code>cuda-toolkit-12.0</code></li></ul></li></ul><p>即便当前系统中不一定存在这些包名，脚本也会使用 <code>|| true</code> 避免因此中断。</p><ol start="6"><li>安装并配置 NVIDIA Container Toolkit</li></ol><ul><li>安装 <code>curl</code> 和 <code>gnupg2</code>；</li><li>从 NVIDIA 官方源下载并导入 GPG key，写入 <code>/usr/share/keyrings/nvidia-container-toolkit-keyring.gpg</code>；</li><li>添加 <code>libnvidia-container</code> 的 apt 软件源，并绑定刚才的 keyring；</li><li>执行 <code>sudo apt-get install -y nvidia-container-toolkit</code>；</li><li>使用 <code>sudo nvidia-ctk runtime configure --runtime=docker</code> 配置 Docker Runtime；</li><li>重启 Docker 服务。</li></ul><p>完成这一步后，Docker 就具备了 <code>--gpus all</code> 访问 GPU 的能力。</p><ol start="7"><li>测试与验证</li></ol><ul><li>宿主机上测试 <code>nvidia-smi</code>，确认驱动加载正常；</li><li>宿主机上测试 <code>nvcc --version</code>，确认 CUDA 编译器安装成功；</li><li>如果系统已安装 Docker：<ul><li>用 <code>nvidia/cuda:12.6.0-base-ubuntu24.04</code> 镜像在容器内运行 <code>nvidia-smi</code>，检查容器内能否看到 GPU。</li></ul></li></ul><p>脚本最后会提示「建议重启系统」。</p><hr><h3 id="2-3-脚本完整内容与使用方式"><a href="#2-3-脚本完整内容与使用方式" class="headerlink" title="2.3 脚本完整内容与使用方式"></a>2.3 脚本完整内容与使用方式</h3><p>脚本内容（可以保存为 <code>install_nvidia_cuda_container.sh</code>）：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#!/usr/bin/env bash</span></span><br><span class="line"><span class="built_in">set</span> -e</span><br><span class="line"></span><br><span class="line"><span class="built_in">echo</span> <span class="string">&quot;===&gt; 1. 禁用 nouveau 驱动&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 创建 / 修正 blacklist 配置文件</span></span><br><span class="line"><span class="built_in">sudo</span> <span class="built_in">mkdir</span> -p /etc/modprobe.d</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> [ -f /etc/modprobe.d/blacklist-nouveau.conf ]; <span class="keyword">then</span></span><br><span class="line">  <span class="built_in">echo</span> <span class="string">&quot;已存在 /etc/modprobe.d/blacklist-nouveau.conf，覆盖更新配置...&quot;</span></span><br><span class="line"><span class="keyword">else</span></span><br><span class="line">  <span class="built_in">echo</span> <span class="string">&quot;创建 /etc/modprobe.d/blacklist-nouveau.conf ...&quot;</span></span><br><span class="line">  <span class="built_in">sudo</span> <span class="built_in">touch</span> /etc/modprobe.d/blacklist-nouveau.conf</span><br><span class="line"><span class="keyword">fi</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">sudo</span> bash -c <span class="string">&#x27;cat &gt;/etc/modprobe.d/blacklist-nouveau.conf &lt;&lt;EOF</span></span><br><span class="line"><span class="string">blacklist nouveau</span></span><br><span class="line"><span class="string">options nouveau modeset=0</span></span><br><span class="line"><span class="string">EOF&#x27;</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">echo</span> <span class="string">&quot;更新 initramfs...&quot;</span></span><br><span class="line"><span class="built_in">sudo</span> update-initramfs -u</span><br><span class="line"></span><br><span class="line"><span class="built_in">echo</span> <span class="string">&quot;===&gt; 2. 更新 apt 源并安装编译工具&quot;</span></span><br><span class="line"><span class="built_in">sudo</span> apt-get update</span><br><span class="line"><span class="built_in">sudo</span> apt-get install -y build-essential</span><br><span class="line"></span><br><span class="line"><span class="built_in">echo</span> <span class="string">&quot;===&gt; 3. 确认并锁定当前内核版本&quot;</span></span><br><span class="line">CURRENT_KVER=<span class="string">&quot;<span class="subst">$(uname -r)</span>&quot;</span></span><br><span class="line"><span class="built_in">echo</span> <span class="string">&quot;当前正在使用的内核版本为: <span class="variable">$&#123;CURRENT_KVER&#125;</span>&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 尝试锁定当前内核相关包，避免自动升级导致驱动失配</span></span><br><span class="line"><span class="keyword">if</span> dpkg -l | grep -q <span class="string">&quot;linux-image-<span class="variable">$&#123;CURRENT_KVER&#125;</span>&quot;</span>; <span class="keyword">then</span></span><br><span class="line">  <span class="built_in">echo</span> <span class="string">&quot;锁定 linux-image-<span class="variable">$&#123;CURRENT_KVER&#125;</span>&quot;</span></span><br><span class="line">  <span class="built_in">sudo</span> apt-mark hold <span class="string">&quot;linux-image-<span class="variable">$&#123;CURRENT_KVER&#125;</span>&quot;</span> || <span class="literal">true</span></span><br><span class="line"><span class="keyword">fi</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> dpkg -l | grep -q <span class="string">&quot;linux-headers-<span class="variable">$&#123;CURRENT_KVER&#125;</span>&quot;</span>; <span class="keyword">then</span></span><br><span class="line">  <span class="built_in">echo</span> <span class="string">&quot;锁定 linux-headers-<span class="variable">$&#123;CURRENT_KVER&#125;</span>&quot;</span></span><br><span class="line">  <span class="built_in">sudo</span> apt-mark hold <span class="string">&quot;linux-headers-<span class="variable">$&#123;CURRENT_KVER&#125;</span>&quot;</span> || <span class="literal">true</span></span><br><span class="line"><span class="keyword">fi</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> dpkg -l | grep -q <span class="string">&quot;linux-modules-extra-<span class="variable">$&#123;CURRENT_KVER&#125;</span>&quot;</span>; <span class="keyword">then</span></span><br><span class="line">  <span class="built_in">echo</span> <span class="string">&quot;锁定 linux-modules-extra-<span class="variable">$&#123;CURRENT_KVER&#125;</span>&quot;</span></span><br><span class="line">  <span class="built_in">sudo</span> apt-mark hold <span class="string">&quot;linux-modules-extra-<span class="variable">$&#123;CURRENT_KVER&#125;</span>&quot;</span> || <span class="literal">true</span></span><br><span class="line"><span class="keyword">fi</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">echo</span> <span class="string">&quot;当前内核信息：&quot;</span></span><br><span class="line"><span class="built_in">uname</span> -a || <span class="literal">true</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">echo</span> <span class="string">&quot;===&gt; 4. 查询并安装推荐的 Nvidia 驱动&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">sudo</span> apt-get update</span><br><span class="line"></span><br><span class="line"><span class="comment"># 尝试列出可用驱动</span></span><br><span class="line"><span class="keyword">if</span> <span class="built_in">command</span> -v ubuntu-drivers &gt;/dev/null 2&gt;&amp;1; <span class="keyword">then</span></span><br><span class="line">  <span class="built_in">echo</span> <span class="string">&quot;检测可用的 Nvidia 驱动 (ubuntu-drivers devices)...&quot;</span></span><br><span class="line">  ubuntu-drivers devices || <span class="literal">true</span></span><br><span class="line"></span><br><span class="line">  <span class="comment"># 自动解析带 recommended 标记的驱动包名</span></span><br><span class="line">  RECOMMENDED_DRIVER=<span class="string">&quot;<span class="subst">$(ubuntu-drivers devices 2&gt;/dev/null | awk &#x27;/recommended/ &#123;print $3; exit&#125;&#x27;)</span>&quot;</span></span><br><span class="line"></span><br><span class="line">  <span class="keyword">if</span> [ -n <span class="string">&quot;<span class="variable">$RECOMMENDED_DRIVER</span>&quot;</span> ]; <span class="keyword">then</span></span><br><span class="line">    <span class="built_in">echo</span> <span class="string">&quot;检测到推荐的 Nvidia 驱动为: <span class="variable">$&#123;RECOMMENDED_DRIVER&#125;</span>&quot;</span></span><br><span class="line">    <span class="built_in">echo</span> <span class="string">&quot;开始安装 <span class="variable">$&#123;RECOMMENDED_DRIVER&#125;</span> ...&quot;</span></span><br><span class="line">    <span class="built_in">sudo</span> apt-get install -y <span class="string">&quot;<span class="variable">$RECOMMENDED_DRIVER</span>&quot;</span></span><br><span class="line">  <span class="keyword">else</span></span><br><span class="line">    <span class="built_in">echo</span> <span class="string">&quot;未能从 ubuntu-drivers devices 输出中解析到 recommended 驱动。&quot;</span></span><br><span class="line">    <span class="built_in">echo</span> <span class="string">&quot;你可以稍后手动运行 &#x27;ubuntu-drivers devices&#x27; 查看并选择合适的驱动安装。&quot;</span></span><br><span class="line">  <span class="keyword">fi</span></span><br><span class="line"><span class="keyword">else</span></span><br><span class="line">  <span class="built_in">echo</span> <span class="string">&quot;未找到 ubuntu-drivers 命令（可能未安装 ubuntu-drivers-common 包），尝试安装...&quot;</span></span><br><span class="line">  <span class="built_in">sudo</span> apt-get install -y ubuntu-drivers-common || <span class="literal">true</span></span><br><span class="line"></span><br><span class="line">  <span class="keyword">if</span> <span class="built_in">command</span> -v ubuntu-drivers &gt;/dev/null 2&gt;&amp;1; <span class="keyword">then</span></span><br><span class="line">    <span class="built_in">echo</span> <span class="string">&quot;再次检测可用的 Nvidia 驱动...&quot;</span></span><br><span class="line">    ubuntu-drivers devices || <span class="literal">true</span></span><br><span class="line">    RECOMMENDED_DRIVER=<span class="string">&quot;<span class="subst">$(ubuntu-drivers devices 2&gt;/dev/null | awk &#x27;/recommended/ &#123;print $3; exit&#125;&#x27;)</span>&quot;</span></span><br><span class="line">    <span class="keyword">if</span> [ -n <span class="string">&quot;<span class="variable">$RECOMMENDED_DRIVER</span>&quot;</span> ]; <span class="keyword">then</span></span><br><span class="line">      <span class="built_in">echo</span> <span class="string">&quot;检测到推荐的 Nvidia 驱动为: <span class="variable">$&#123;RECOMMENDED_DRIVER&#125;</span>&quot;</span></span><br><span class="line">      <span class="built_in">echo</span> <span class="string">&quot;开始安装 <span class="variable">$&#123;RECOMMENDED_DRIVER&#125;</span> ...&quot;</span></span><br><span class="line">      <span class="built_in">sudo</span> apt-get install -y <span class="string">&quot;<span class="variable">$RECOMMENDED_DRIVER</span>&quot;</span></span><br><span class="line">    <span class="keyword">else</span></span><br><span class="line">      <span class="built_in">echo</span> <span class="string">&quot;仍未解析到 recommended 驱动，请手动选择并安装合适的 Nvidia 驱动。&quot;</span></span><br><span class="line">    <span class="keyword">fi</span></span><br><span class="line">  <span class="keyword">else</span></span><br><span class="line">    <span class="built_in">echo</span> <span class="string">&quot;仍然无法使用 ubuntu-drivers 工具，请手动安装合适的 Nvidia 驱动（例如 sudo apt-get install nvidia-driver-XXX）。&quot;</span></span><br><span class="line">  <span class="keyword">fi</span></span><br><span class="line"><span class="keyword">fi</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">echo</span> <span class="string">&quot;驱动安装完成后建议重启以加载驱动。&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">echo</span> <span class="string">&quot;===&gt; 5. 安装 CUDA 工具链（nvidia-cuda-toolkit）&quot;</span></span><br><span class="line"><span class="built_in">sudo</span> apt-get install -y nvidia-cuda-toolkit</span><br><span class="line"></span><br><span class="line"><span class="built_in">echo</span> <span class="string">&quot;尝试固定 CUDA 工具链版本（如果存在对应包名）&quot;</span></span><br><span class="line"><span class="built_in">sudo</span> apt-mark hold cuda-toolkit-12-0 || <span class="literal">true</span></span><br><span class="line"><span class="built_in">sudo</span> apt-mark hold cuda-toolkit-12.0 || <span class="literal">true</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">echo</span> <span class="string">&quot;===&gt; 6. 安装 NVIDIA Container Toolkit (nvidia-container-toolkit)&quot;</span></span><br><span class="line"><span class="comment"># 自动化写 key 和源</span></span><br><span class="line"><span class="built_in">sudo</span> apt-get install -y curl gnupg2</span><br><span class="line"></span><br><span class="line"><span class="built_in">echo</span> <span class="string">&quot;添加 NVIDIA Container Toolkit GPG key...&quot;</span></span><br><span class="line">curl -fsSL https://nvidia.github.io/libnvidia-container/gpgkey | \</span><br><span class="line">  <span class="built_in">sudo</span> gpg --dearmor -o /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg</span><br><span class="line"></span><br><span class="line"><span class="built_in">echo</span> <span class="string">&quot;添加 NVIDIA Container Toolkit apt 源...&quot;</span></span><br><span class="line">curl -s -L https://nvidia.github.io/libnvidia-container/stable/deb/nvidia-container-toolkit.list | \</span><br><span class="line">  sed <span class="string">&#x27;s#deb https://#deb [signed-by=/usr/share/keyrings/nvidia-container-toolkit-keyring.gpg] https://#g&#x27;</span> | \</span><br><span class="line">  <span class="built_in">sudo</span> <span class="built_in">tee</span> /etc/apt/sources.list.d/nvidia-container-toolkit.list &gt; /dev/null</span><br><span class="line"></span><br><span class="line"><span class="built_in">sudo</span> apt-get update</span><br><span class="line"><span class="built_in">sudo</span> apt-get install -y nvidia-container-toolkit</span><br><span class="line"></span><br><span class="line"><span class="built_in">echo</span> <span class="string">&quot;配置 Docker 使用 nvidia-container-toolkit 作为 runtime...&quot;</span></span><br><span class="line"><span class="built_in">sudo</span> nvidia-ctk runtime configure --runtime=docker</span><br><span class="line"><span class="built_in">sudo</span> systemctl restart docker</span><br><span class="line"></span><br><span class="line"><span class="built_in">echo</span> <span class="string">&quot;===&gt; 7. 测试 nvidia-smi（宿主机）&quot;</span></span><br><span class="line"><span class="keyword">if</span> <span class="built_in">command</span> -v nvidia-smi &gt;/dev/null 2&gt;&amp;1; <span class="keyword">then</span></span><br><span class="line">  nvidia-smi || <span class="built_in">echo</span> <span class="string">&quot;nvidia-smi 执行失败，请检查驱动是否正确加载（可能需要重启后再试）&quot;</span></span><br><span class="line"><span class="keyword">else</span></span><br><span class="line">  <span class="built_in">echo</span> <span class="string">&quot;未找到 nvidia-smi 命令，请确认 Nvidia 驱动是否安装成功。&quot;</span></span><br><span class="line"><span class="keyword">fi</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">echo</span> <span class="string">&quot;===&gt; 8. 测试 nvcc（CUDA 编译器）&quot;</span></span><br><span class="line"><span class="keyword">if</span> <span class="built_in">command</span> -v nvcc &gt;/dev/null 2&gt;&amp;1; <span class="keyword">then</span></span><br><span class="line">  nvcc --version || <span class="literal">true</span></span><br><span class="line"><span class="keyword">else</span></span><br><span class="line">  <span class="built_in">echo</span> <span class="string">&quot;未找到 nvcc（可能需要重启或检查 nvidia-cuda-toolkit 是否正常安装）&quot;</span></span><br><span class="line"><span class="keyword">fi</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">echo</span> <span class="string">&quot;===&gt; 9. 使用带 GPU 的 Docker 容器测试 nvidia-smi&quot;</span></span><br><span class="line"><span class="keyword">if</span> <span class="built_in">command</span> -v docker &gt;/dev/null 2&gt;&amp;1; <span class="keyword">then</span></span><br><span class="line">  <span class="built_in">echo</span> <span class="string">&quot;拉取并运行 nvidia/cuda:12.6.0-base-ubuntu24.04 镜像测试 GPU...&quot;</span></span><br><span class="line">  <span class="built_in">sudo</span> docker run --<span class="built_in">rm</span> --gpus all nvidia/cuda:12.6.0-base-ubuntu24.04 nvidia-smi || \</span><br><span class="line">    <span class="built_in">echo</span> <span class="string">&quot;容器内 nvidia-smi 执行失败，请检查 nvidia-container-toolkit / Docker 配置。&quot;</span></span><br><span class="line"><span class="keyword">else</span></span><br><span class="line">  <span class="built_in">echo</span> <span class="string">&quot;未安装 Docker，本脚本只完成 nvidia-container-toolkit 安装，Docker 测试略过。&quot;</span></span><br><span class="line"><span class="keyword">fi</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">echo</span> <span class="string">&quot;全部步骤执行完成。建议重启系统：sudo reboot&quot;</span></span><br></pre></td></tr></table></figure><p>使用方式：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">chmod</span> +x install_nvidia_cuda_container.sh</span><br><span class="line">./install_nvidia_cuda_container.sh</span><br><span class="line"><span class="built_in">sudo</span> reboot</span><br></pre></td></tr></table></figure><p>重启后建议手动执行一次：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">nvidia-smi</span><br><span class="line">nvcc --version</span><br></pre></td></tr></table></figure><p>确认驱动和 CUDA 工具链工作正常。</p><hr><h2 id="三、Ollama-部署脚本：run-ollama-sh"><a href="#三、Ollama-部署脚本：run-ollama-sh" class="headerlink" title="三、Ollama 部署脚本：run_ollama.sh"></a>三、Ollama 部署脚本：run_ollama.sh</h2><h3 id="3-1-设计目标"><a href="#3-1-设计目标" class="headerlink" title="3.1 设计目标"></a>3.1 设计目标</h3><p>这份脚本用来自动化如下操作：</p><ul><li>确认 Docker 已安装；</li><li>拉取最新的 GPU 版 Ollama 镜像；</li><li>停止并删除已有的 <code>ollama</code> 容器（如果存在），避免命名冲突；</li><li>启动一个新的 Ollama 容器：<ul><li>开启 GPU 支持；</li><li>使用 Docker 卷 <code>ollama</code> 挂载到容器内 <code>/root/.ollama</code>，用于持久化模型数据；</li><li>映射 11434 端口；</li></ul></li><li>提供交互选项，一键拉取常用的 Qwen3 系列模型（<code>qwen3-vl:8b</code> &#x2F; <code>qwen3-vl:30b</code> &#x2F; <code>qwen3-coder:30b</code>）。</li></ul><p>这样，在已经配置好 GPU + Docker 环境的前提下，只需运行脚本，就可以快速得到一个可用的 Ollama 服务。</p><hr><h3 id="3-2-脚本逻辑说明"><a href="#3-2-脚本逻辑说明" class="headerlink" title="3.2 脚本逻辑说明"></a>3.2 脚本逻辑说明</h3><p>核心步骤如下：</p><ol><li>检查 Docker 是否安装</li></ol><ul><li>通过 <code>command -v docker</code> 检查 Docker 命令是否存在；</li><li>如果不存在，则提示先安装 Docker 并退出脚本（避免后续错误）。</li></ul><ol start="2"><li>拉取 Ollama 镜像</li></ol><ul><li><p>执行：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> docker pull ollama/ollama:latest</span><br></pre></td></tr></table></figure></li><li><p>始终确保使用的是最新版镜像。</p></li></ul><ol start="3"><li>清理已有 <code>ollama</code> 容器（如果存在）</li></ol><ul><li>通过 <code>sudo docker ps -a --format &#39;&#123;&#123;.Names&#125;&#125;&#39; | grep -w ollama</code> 检查；</li><li>如果找到同名容器，先 <code>docker stop</code>，再 <code>docker rm</code>，错误不导致脚本中断。</li></ul><ol start="4"><li>启动新的 Ollama 容器</li></ol><ul><li><p>使用命令：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> docker run -d \</span><br><span class="line">  --gpus=all \</span><br><span class="line">  -v ollama:/root/.ollama \</span><br><span class="line">  -p 11434:11434 \</span><br><span class="line">  --name ollama \</span><br><span class="line">  ollama/ollama:latest</span><br></pre></td></tr></table></figure></li><li><p>参数说明：</p><ul><li><code>--gpus=all</code>：启用 GPU；</li><li><code>-v ollama:/root/.ollama</code>：使用名为 <code>ollama</code> 的 Docker 卷保存模型和配置；</li><li><code>-p 11434:11434</code>：暴露 Ollama 默认 API 端口；</li><li><code>--name ollama</code>：固定容器名。</li></ul></li></ul><ol start="5"><li>可选：拉取 Qwen3 系列模型</li></ol><ul><li>提示是否自动拉取模型；</li><li>如果用户选择 <code>y</code>：<ul><li>依次执行：<ul><li><code>ollama pull qwen3-vl:8b</code></li><li><code>ollama pull qwen3-vl:30b</code></li><li><code>ollama pull qwen3-coder:30b</code></li></ul></li></ul></li><li>如果选择 <code>n</code> 或直接回车，则仅提示后续可以手动执行这些命令。</li></ul><hr><h3 id="3-3-脚本完整内容与使用方式"><a href="#3-3-脚本完整内容与使用方式" class="headerlink" title="3.3 脚本完整内容与使用方式"></a>3.3 脚本完整内容与使用方式</h3><p>脚本内容（保存为 <code>run_ollama.sh</code>）：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#!/usr/bin/env bash</span></span><br><span class="line"><span class="built_in">set</span> -e</span><br><span class="line"></span><br><span class="line"><span class="built_in">echo</span> <span class="string">&quot;===&gt; 1. 检查 docker 是否安装&quot;</span></span><br><span class="line"><span class="keyword">if</span> ! <span class="built_in">command</span> -v docker &gt;/dev/null 2&gt;&amp;1; <span class="keyword">then</span></span><br><span class="line">  <span class="built_in">echo</span> <span class="string">&quot;未检测到 docker，请先运行 install_docker.sh 安装 Docker。&quot;</span></span><br><span class="line">  <span class="built_in">exit</span> 1</span><br><span class="line"><span class="keyword">fi</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">echo</span> <span class="string">&quot;===&gt; 2. 拉取 Ollama 镜像（GPU 版）&quot;</span></span><br><span class="line"><span class="built_in">sudo</span> docker pull ollama/ollama:latest</span><br><span class="line"></span><br><span class="line"><span class="built_in">echo</span> <span class="string">&quot;===&gt; 3. 停止并删除已有的 ollama 容器（若存在）&quot;</span></span><br><span class="line"><span class="keyword">if</span> <span class="built_in">sudo</span> docker ps -a --format <span class="string">&#x27;&#123;&#123;.Names&#125;&#125;&#x27;</span> | grep -w ollama &gt;/dev/null 2&gt;&amp;1; <span class="keyword">then</span></span><br><span class="line">  <span class="built_in">sudo</span> docker stop ollama || <span class="literal">true</span></span><br><span class="line">  <span class="built_in">sudo</span> docker <span class="built_in">rm</span> ollama || <span class="literal">true</span></span><br><span class="line"><span class="keyword">fi</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">echo</span> <span class="string">&quot;===&gt; 4. 运行 Ollama 容器，挂载数据卷 &#x27;ollama&#x27; 到 /root/.ollama&quot;</span></span><br><span class="line"><span class="built_in">sudo</span> docker run -d \</span><br><span class="line">  --gpus=all \</span><br><span class="line">  -v ollama:/root/.ollama \</span><br><span class="line">  -p 11434:11434 \</span><br><span class="line">  --name ollama \</span><br><span class="line">  ollama/ollama:latest</span><br><span class="line"></span><br><span class="line"><span class="built_in">echo</span> <span class="string">&quot;Ollama 容器已启动。检查状态：&quot;</span></span><br><span class="line"><span class="built_in">sudo</span> docker ps | grep ollama || <span class="built_in">echo</span> <span class="string">&quot;注意：未看到 ollama 容器，请检查日志。&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">echo</span> <span class="string">&quot;===&gt; 5. 可选：自动拉取常用模型（Qwen3 系列）&quot;</span></span><br><span class="line"><span class="built_in">read</span> -p <span class="string">&quot;是否自动拉取 qwen3-vl:8b / qwen3-vl:30b / qwen3-coder:30b 模型？(y/N) &quot;</span> yn</span><br><span class="line"><span class="keyword">case</span> <span class="string">&quot;<span class="variable">$yn</span>&quot;</span> <span class="keyword">in</span></span><br><span class="line">  [Yy]* )</span><br><span class="line">    <span class="built_in">echo</span> <span class="string">&quot;开始拉取模型（时间较长，取决于网络）：&quot;</span></span><br><span class="line">    <span class="built_in">sudo</span> docker <span class="built_in">exec</span> -it ollama ollama pull qwen3-vl:8b || <span class="literal">true</span></span><br><span class="line">    <span class="built_in">sudo</span> docker <span class="built_in">exec</span> -it ollama ollama pull qwen3-vl:30b || <span class="literal">true</span></span><br><span class="line">    <span class="built_in">sudo</span> docker <span class="built_in">exec</span> -it ollama ollama pull qwen3-coder:30b || <span class="literal">true</span></span><br><span class="line">    <span class="built_in">echo</span> <span class="string">&quot;模型拉取流程结束（如拉取失败可查看 docker logs ollama）。&quot;</span></span><br><span class="line">    ;;</span><br><span class="line">  * )</span><br><span class="line">    <span class="built_in">echo</span> <span class="string">&quot;跳过自动拉取模型。你可以稍后手工执行：&quot;</span></span><br><span class="line">    <span class="built_in">echo</span> <span class="string">&quot;  sudo docker exec -it ollama ollama pull qwen3-vl:8b&quot;</span></span><br><span class="line">    <span class="built_in">echo</span> <span class="string">&quot;  sudo docker exec -it ollama ollama pull qwen3-vl:30b&quot;</span></span><br><span class="line">    <span class="built_in">echo</span> <span class="string">&quot;  sudo docker exec -it ollama ollama pull qwen3-coder:30b&quot;</span></span><br><span class="line">    ;;</span><br><span class="line"><span class="keyword">esac</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">echo</span> <span class="string">&quot;===&gt; 完成。你可以通过 http://&lt;服务器IP&gt;:11434 访问 Ollama API。&quot;</span></span><br></pre></td></tr></table></figure><p>使用示例：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">chmod</span> +x run_ollama.sh</span><br><span class="line">./run_ollama.sh</span><br></pre></td></tr></table></figure><p>完成后可以：</p><ul><li>通过 <code>sudo docker logs ollama</code> 查看运行日志；</li><li>通过 <code>http://&lt;服务器IP&gt;:11434</code> 调用 Ollama API。</li></ul><hr><h2 id="四、Hexo-博客脚本：setup-hexo-blog-sh"><a href="#四、Hexo-博客脚本：setup-hexo-blog-sh" class="headerlink" title="四、Hexo 博客脚本：setup_hexo_blog.sh"></a>四、Hexo 博客脚本：setup_hexo_blog.sh</h2><h3 id="4-1-设计目标"><a href="#4-1-设计目标" class="headerlink" title="4.1 设计目标"></a>4.1 设计目标</h3><p>这份脚本把「搭建 Hexo 博客」过程中重复的步骤自动化，主要包括：</p><ul><li>安装 <code>nvm</code>（如果未安装）；</li><li>通过 <code>nvm</code> 安装 Node LTS 并设为默认版本；</li><li>全局安装 <code>hexo-cli</code>；</li><li>创建或重建博客目录（默认 <code>$HOME/hexo</code>）；</li><li>初始化 Hexo（如需要），安装项目依赖；</li><li>安装 <code>hexo-deployer-git</code> 插件；</li><li>配置 Git 全局用户名和邮箱（若尚未配置）；</li><li>提示并辅助修改 <code>_config.yml</code> 中的 <code>deploy</code> 配置；</li><li>自动执行 <code>hexo clean</code> 和 <code>hexo generate</code>，并提供选项是否立即执行 <code>hexo deploy</code>。</li></ul><hr><h3 id="4-2-脚本逻辑说明"><a href="#4-2-脚本逻辑说明" class="headerlink" title="4.2 脚本逻辑说明"></a>4.2 脚本逻辑说明</h3><ol><li>安装并加载 nvm</li></ol><ul><li>如果 <code>~/.nvm</code> 目录存在，认为已经安装过 nvm，跳过安装；</li><li>否则，通过官方脚本安装 nvm；</li><li>设置 <code>NVM_DIR</code> 并 <code>source &quot;$NVM_DIR/nvm.sh&quot;</code>，确保后续在当前脚本环境中可以直接使用 <code>nvm</code> 命令。</li></ul><ol start="2"><li>安装 Node LTS 并设为默认</li></ol><ul><li><p>执行：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">nvm install --lts</span><br><span class="line">nvm use --lts</span><br><span class="line">nvm <span class="built_in">alias</span> default <span class="string">&quot;lts/*&quot;</span></span><br></pre></td></tr></table></figure></li><li><p>打印当前 <code>node -v</code> 和 <code>npm -v</code> 做确认。</p></li></ul><ol start="3"><li>全局安装 <code>hexo-cli</code></li></ol><ul><li><p>通过 npm 全局安装：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm install -g hexo-cli</span><br></pre></td></tr></table></figure></li></ul><ol start="4"><li>创建或重新初始化 Hexo 博客目录</li></ol><ul><li>使用变量 <code>BLOG_DIR=&quot;$HOME/hexo&quot;</code>；</li><li>若目录已存在：<ul><li>询问是否删除并重建；</li><li>如果选择删除，则 <code>rm -rf</code> 并重新创建；</li><li>如果保留，则不再执行 <code>hexo init</code>，只进入该目录并继续后续步骤。</li></ul></li><li>若目录不存在：<ul><li>创建目录并执行 <code>hexo init .</code> 初始化项目。</li></ul></li></ul><ol start="5"><li>安装项目依赖和 <code>hexo-deployer-git</code></li></ol><ul><li>在博客目录执行 <code>npm install</code>，安装 <code>package.json</code> 中的依赖；</li><li>执行 <code>npm install hexo-deployer-git --save</code>，添加 Git 部署插件。</li></ul><ol start="6"><li>配置 Git 全局用户信息（如未设置）</li></ol><ul><li>如果 <code>git config --global user.email</code> 未设置，则写入默认值 <code>your_email@example.com</code>；</li><li>如果 <code>git config --global user.name</code> 未设置，则写入默认值 <code>YourName</code>；</li><li>后续应手动修改为自己的邮箱和用户名。</li></ul><ol start="7"><li>提示编辑 <code>_config.yml</code> 的 deploy 部分</li></ol><ul><li><p>脚本打印一个示例配置：</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">deploy:</span></span><br><span class="line">  <span class="attr">type:</span> <span class="string">git</span></span><br><span class="line">  <span class="attr">repo:</span> <span class="string">git@your.git.repo:your/blog.git</span></span><br><span class="line">  <span class="attr">branch:</span> <span class="string">main</span></span><br></pre></td></tr></table></figure></li><li><p>询问是否现在打开 <code>_config.yml</code> 进行编辑；</p></li><li><p>如果选择 <code>y</code>，则用 <code>$&#123;EDITOR:-vim&#125;</code> 打开配置文件；</p></li><li><p>否则提示稍后手动编辑。</p></li></ul><ol start="8"><li>构建并可选执行 <code>hexo deploy</code></li></ol><ul><li>执行 <code>hexo clean</code> 和 <code>hexo generate</code>；</li><li>询问是否现在执行 <code>hexo deploy</code>：<ul><li>如果选择执行，出错时会提示检查 <code>_config.yml</code> 和 Git 权限；</li><li>如果跳过，则提示后续可以在博客目录手动执行 <code>hexo clean &amp;&amp; hexo generate &amp;&amp; hexo deploy</code>。</li></ul></li></ul><ol start="9"><li>theam</li></ol><ul><li><a href="https://butterfly.js.org/">https://butterfly.js.org/</a></li></ul><hr><h3 id="4-3-脚本完整内容与使用方式"><a href="#4-3-脚本完整内容与使用方式" class="headerlink" title="4.3 脚本完整内容与使用方式"></a>4.3 脚本完整内容与使用方式</h3><p>脚本内容（保存为 <code>setup_hexo_blog.sh</code>）：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#!/usr/bin/env bash</span></span><br><span class="line"><span class="built_in">set</span> -e</span><br><span class="line"></span><br><span class="line">BLOG_DIR=<span class="string">&quot;<span class="variable">$HOME</span>/hexo&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">echo</span> <span class="string">&quot;===&gt; 1. 安装 nvm（如果尚未安装）&quot;</span></span><br><span class="line"><span class="keyword">if</span> [ -d <span class="string">&quot;<span class="variable">$HOME</span>/.nvm&quot;</span> ]; <span class="keyword">then</span></span><br><span class="line">  <span class="built_in">echo</span> <span class="string">&quot;检测到 ~/.nvm 已存在，跳过安装。&quot;</span></span><br><span class="line"><span class="keyword">else</span></span><br><span class="line">  curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.1/install.sh | bash</span><br><span class="line"><span class="keyword">fi</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 加载 nvm（脚本执行期间生效）</span></span><br><span class="line"><span class="built_in">export</span> NVM_DIR=<span class="string">&quot;<span class="variable">$HOME</span>/.nvm&quot;</span></span><br><span class="line"><span class="comment"># shellcheck disable=SC1090</span></span><br><span class="line">[ -s <span class="string">&quot;<span class="variable">$NVM_DIR</span>/nvm.sh&quot;</span> ] &amp;&amp; . <span class="string">&quot;<span class="variable">$NVM_DIR</span>/nvm.sh&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">echo</span> <span class="string">&quot;===&gt; 2. 安装 Node LTS 并设为默认&quot;</span></span><br><span class="line">nvm install --lts</span><br><span class="line">nvm use --lts</span><br><span class="line">nvm <span class="built_in">alias</span> default <span class="string">&quot;lts/*&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">echo</span> <span class="string">&quot;当前 Node 版本：&quot;</span></span><br><span class="line">node -v</span><br><span class="line">npm -v</span><br><span class="line"></span><br><span class="line"><span class="built_in">echo</span> <span class="string">&quot;===&gt; 3. 全局安装 hexo-cli&quot;</span></span><br><span class="line">npm install -g hexo-cli</span><br><span class="line"></span><br><span class="line"><span class="built_in">echo</span> <span class="string">&quot;===&gt; 4. 创建或重新初始化 Hexo 博客目录：<span class="variable">$BLOG_DIR</span>&quot;</span></span><br><span class="line"><span class="keyword">if</span> [ -d <span class="string">&quot;<span class="variable">$BLOG_DIR</span>&quot;</span> ]; <span class="keyword">then</span></span><br><span class="line">  <span class="built_in">read</span> -p <span class="string">&quot;目录 <span class="variable">$BLOG_DIR</span> 已存在，是否删除并重建？(y/N) &quot;</span> yn</span><br><span class="line">  <span class="keyword">case</span> <span class="string">&quot;<span class="variable">$yn</span>&quot;</span> <span class="keyword">in</span></span><br><span class="line">    [Yy]* )</span><br><span class="line">      <span class="built_in">rm</span> -rf <span class="string">&quot;<span class="variable">$BLOG_DIR</span>&quot;</span></span><br><span class="line">      ;;</span><br><span class="line">    * )</span><br><span class="line">      <span class="built_in">echo</span> <span class="string">&quot;保留现有目录，将跳过 hexo init，如需全新初始化请先删除该目录。&quot;</span></span><br><span class="line">      ;;</span><br><span class="line">  <span class="keyword">esac</span></span><br><span class="line"><span class="keyword">fi</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> [ ! -d <span class="string">&quot;<span class="variable">$BLOG_DIR</span>&quot;</span> ]; <span class="keyword">then</span></span><br><span class="line">  <span class="built_in">mkdir</span> -p <span class="string">&quot;<span class="variable">$BLOG_DIR</span>&quot;</span></span><br><span class="line">  <span class="built_in">cd</span> <span class="string">&quot;<span class="variable">$BLOG_DIR</span>&quot;</span></span><br><span class="line">  <span class="built_in">echo</span> <span class="string">&quot;初始化 Hexo 项目...&quot;</span></span><br><span class="line">  hexo init .</span><br><span class="line"><span class="keyword">else</span></span><br><span class="line">  <span class="built_in">cd</span> <span class="string">&quot;<span class="variable">$BLOG_DIR</span>&quot;</span></span><br><span class="line"><span class="keyword">fi</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">echo</span> <span class="string">&quot;===&gt; 5. 安装项目依赖&quot;</span></span><br><span class="line">npm install</span><br><span class="line"></span><br><span class="line"><span class="built_in">echo</span> <span class="string">&quot;===&gt; 6. 安装 hexo-deployer-git（用 Git 部署）&quot;</span></span><br><span class="line">npm install hexo-deployer-git --save</span><br><span class="line"></span><br><span class="line"><span class="built_in">echo</span> <span class="string">&quot;===&gt; 7. 配置 Git 全局用户名和邮箱（如已有可跳过）&quot;</span></span><br><span class="line"><span class="keyword">if</span> ! git config --global user.email &gt;/dev/null 2&gt;&amp;1; <span class="keyword">then</span></span><br><span class="line">  git config --global user.email <span class="string">&quot;your_email@example.com&quot;</span></span><br><span class="line">  <span class="built_in">echo</span> <span class="string">&quot;已设置 git user.email 为 your_email@example.com（请按需修改）&quot;</span></span><br><span class="line"><span class="keyword">fi</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> ! git config --global user.name &gt;/dev/null 2&gt;&amp;1; <span class="keyword">then</span></span><br><span class="line">  git config --global user.name <span class="string">&quot;YourName&quot;</span></span><br><span class="line">  <span class="built_in">echo</span> <span class="string">&quot;已设置 git user.name 为 YourName（请按需修改）&quot;</span></span><br><span class="line"><span class="keyword">fi</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">echo</span> <span class="string">&quot;===&gt; 8. 提示修改 _config.yml 中的部署配置&quot;</span></span><br><span class="line"><span class="built_in">echo</span> <span class="string">&quot;请编辑 <span class="variable">$BLOG_DIR</span>/_config.yml 中的 deploy 部分，例如：&quot;</span></span><br><span class="line"><span class="built_in">cat</span> &lt;&lt;<span class="string">&#x27;EOF&#x27;</span></span><br><span class="line"></span><br><span class="line">deploy:</span><br><span class="line">  <span class="built_in">type</span>: git</span><br><span class="line">  repo: git@your.git.repo:your/blog.git</span><br><span class="line">  branch: main</span><br><span class="line"></span><br><span class="line">EOF</span><br><span class="line"></span><br><span class="line"><span class="built_in">read</span> -p <span class="string">&quot;现在要打开 _config.yml 进行编辑吗？(y/N) &quot;</span> yn</span><br><span class="line"><span class="keyword">case</span> <span class="string">&quot;<span class="variable">$yn</span>&quot;</span> <span class="keyword">in</span></span><br><span class="line">  [Yy]* )</span><br><span class="line">    <span class="variable">$&#123;EDITOR:-vim&#125;</span> <span class="string">&quot;<span class="variable">$BLOG_DIR</span>/_config.yml&quot;</span></span><br><span class="line">    ;;</span><br><span class="line">  * )</span><br><span class="line">    <span class="built_in">echo</span> <span class="string">&quot;已跳过编辑，请稍后手动修改 _config.yml。&quot;</span></span><br><span class="line">    ;;</span><br><span class="line"><span class="keyword">esac</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">echo</span> <span class="string">&quot;===&gt; 9. 构建并尝试部署（如果 deploy 配置已正确）&quot;</span></span><br><span class="line">hexo clean</span><br><span class="line">hexo generate</span><br><span class="line"></span><br><span class="line"><span class="built_in">read</span> -p <span class="string">&quot;是否现在执行 hexo deploy？(y/N) &quot;</span> yn2</span><br><span class="line"><span class="keyword">case</span> <span class="string">&quot;<span class="variable">$yn2</span>&quot;</span> <span class="keyword">in</span></span><br><span class="line">  [Yy]* )</span><br><span class="line">    hexo deploy || <span class="built_in">echo</span> <span class="string">&quot;hexo deploy 失败，请检查 _config.yml 的 deploy 配置和 Git 仓库访问权限。&quot;</span></span><br><span class="line">    ;;</span><br><span class="line">  * )</span><br><span class="line">    <span class="built_in">echo</span> <span class="string">&quot;已跳过 deploy，你之后可以在 <span class="variable">$BLOG_DIR</span> 目录执行：&quot;</span></span><br><span class="line">    <span class="built_in">echo</span> <span class="string">&quot;  hexo clean &amp;&amp; hexo generate &amp;&amp; hexo deploy&quot;</span></span><br><span class="line">    ;;</span><br><span class="line"><span class="keyword">esac</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">echo</span> <span class="string">&quot;===&gt; Hexo 博客环境初始化完成。&quot;</span></span><br><span class="line"><span class="built_in">echo</span> <span class="string">&quot;如需本地预览，可在 <span class="variable">$BLOG_DIR</span> 中执行：hexo server&quot;</span></span><br></pre></td></tr></table></figure><p>使用方式：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">chmod</span> +x setup_hexo_blog.sh</span><br><span class="line">./setup_hexo_blog.sh</span><br></pre></td></tr></table></figure><p>执行过程中注意：</p><ul><li>将脚本中的 <code>your_email@example.com</code> 和 <code>YourName</code> 换成自己的 Git 配置；</li><li>在编辑 <code>_config.yml</code> 时，把 <code>deploy.repo</code> 换成自己的仓库地址，<code>branch</code> 换成实际使用的分支。</li></ul><hr><h2 id="五、推荐使用顺序（只围绕三个脚本）"><a href="#五、推荐使用顺序（只围绕三个脚本）" class="headerlink" title="五、推荐使用顺序（只围绕三个脚本）"></a>五、推荐使用顺序（只围绕三个脚本）</h2><p>如果你只关心这三份脚本，那么在一台新服务器上的典型执行顺序可以简化为：</p><ol><li><p>GPU 环境准备</p><ul><li><p>上传并执行 GPU 脚本：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">chmod</span> +x install_nvidia_cuda_container.sh</span><br><span class="line">./install_nvidia_cuda_container.sh</span><br><span class="line"><span class="built_in">sudo</span> reboot</span><br></pre></td></tr></table></figure></li><li><p>重启后验证：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">nvidia-smi</span><br><span class="line">nvcc --version</span><br></pre></td></tr></table></figure></li></ul></li><li><p>安装 Docker（不在本文脚本中）</p><ul><li><p>按 Ubuntu 官方文档或自己写的 <code>install_docker.sh</code> 安装 Docker；</p></li><li><p>测试 GPU 容器：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> docker run --<span class="built_in">rm</span> --gpus all nvidia/cuda:12.6.0-base-ubuntu24.04 nvidia-smi</span><br></pre></td></tr></table></figure></li></ul></li><li><p>启动 Ollama 服务</p><ul><li><p>上传并执行 <code>run_ollama.sh</code>：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">chmod</span> +x run_ollama.sh</span><br><span class="line">./run_ollama.sh</span><br></pre></td></tr></table></figure></li><li><p>按提示选择是否自动拉取 Qwen3 系列模型；</p></li><li><p>访问 <code>http://&lt;服务器IP&gt;:11434</code> 测试。</p></li></ul></li><li><p>搭建 Hexo 博客</p><ul><li><p>上传并执行 <code>setup_hexo_blog.sh</code>：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">chmod</span> +x setup_hexo_blog.sh</span><br><span class="line">./setup_hexo_blog.sh</span><br></pre></td></tr></table></figure></li><li><p>在脚本提示阶段完成：</p><ul><li>Git 用户名 &#x2F; 邮箱的确认；</li><li><code>_config.yml</code> 中 <code>deploy</code> 仓库的设置；</li></ul></li><li><p>此后即可通过：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">hexo clean &amp;&amp; hexo generate &amp;&amp; hexo deploy</span><br></pre></td></tr></table></figure><p>更新博客。</p></li></ul></li></ol><hr><h2 id="六、结语"><a href="#六、结语" class="headerlink" title="六、结语"></a>六、结语</h2><p>这三份脚本浓缩的是我在搭建环境时真正「可重复」的步骤，它们分别负责：</p><ul><li>GPU 驱动 + CUDA + Docker GPU Runtime 的基础环境；</li><li>Ollama 服务及常用模型的快速拉起；</li><li>Hexo 博客的 Node 环境、项目初始化与 Git 部署配置。</li></ul><p>相比逐条翻查 <code>bash_history</code>，直接维护这几份脚本要省心得多。未来如果发行版、驱动或工具链有变化，可以在这几份脚本上迭代，而不必从头回忆所有命令。</p><p>最后再强调一次：<br>本文由 AI 结合当前脚本内容自动生成说明，细节难免会随着时间过时。在正式执行前，请务必：</p><ul><li>先阅读脚本本身；</li><li>检查其中的版本号、软件源和命令是否仍然适用于当时的 Ubuntu 版本；</li><li>在非关键环境中先做一次测试。</li></ul>]]></content>
    
    
      
      
    <summary type="html">&lt;blockquote&gt;
&lt;p&gt;本文由 AI 根据我在服务器上的实际操作记录和三份自动化脚本整理生成，主要用于之后再次部署时参考。&lt;br&gt;请务必在使用前自行检查命令、版本号和配置是否符合当前环境，避免因系统更新或个人环境差异导致问题。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 </summary>
      
    
    
    
    <category term="server" scheme="https://www.helloyiyu.com/categories/server/"/>
    
    
    <category term="server" scheme="https://www.helloyiyu.com/tags/server/"/>
    
    <category term="hexo" scheme="https://www.helloyiyu.com/tags/hexo/"/>
    
    <category term="docker" scheme="https://www.helloyiyu.com/tags/docker/"/>
    
    <category term="code-server" scheme="https://www.helloyiyu.com/tags/code-server/"/>
    
    <category term="ollama" scheme="https://www.helloyiyu.com/tags/ollama/"/>
    
  </entry>
  
</feed>
