[{"content":"主流AI工具使用整理 视频生成 sora2：https://sora.chatgpt.com/explore，需要梯子，内测制，需要邀请码 seedance2.0:https://jimeng.jianying.com/ai-tool/home,无需梯子，内测制，需要会员 音乐生成 suno2：https://suno.com/home，需要梯子，公测，免费，次数有日限 图片生成 liblib：https://www.liblib.art/，无需梯子，有限时间免费 leonardo：https://leonardo.ai/，需要梯子，日有限次数免费 ","date":"2026-02-11T00:00:00Z","image":"https://SJTdreams.github.io/p/%E4%B8%BB%E6%B5%81ai%E5%B7%A5%E5%85%B7%E6%95%B4%E7%90%86/title_hu_5e8cff8d7ae2c1a2.jpg","permalink":"https://SJTdreams.github.io/p/%E4%B8%BB%E6%B5%81ai%E5%B7%A5%E5%85%B7%E6%95%B4%E7%90%86/","title":"主流AI工具整理"},{"content":"Windows 功能虚拟机配置教程（Windows 10） 目录\n[TOC]\n在windows10或windows11的专业版中，配置了系统原生支持的安卓虚拟机系统。本博客仅简要记录开启步骤。\n1.打开控制面板 - 程序与功能 - 启用或关闭Windows功能\n找到虚拟机平台，打勾并确定，等待安装，重启\n2.打开Powershell\n输入set-ExecutionPolicy RemoteSigned 输入Y确认\n打开 https://github.com/MustardChef/WSABuilds/releases ，下载对应版本的WSA工具（如win10可采用https://github.com/MustardChef/WSABuilds/releases/download/Windows_10_2407.40000.4.0_v2/WSA_2407.40000.4.0_x64_Release-Nightly-MindTheGapps-13.0-RemovedAmazon_Windows_10.7z）\n3.对下载的文件进行解压，找到并右键Install.ps1选择使用 PowerShell 运行\n4.通过https://developer.android.google.cn/tools/releases/platform-tools?hl=zh-cn下载适用于 Windows 的 SDK Platform-Tools\n5.此电脑 -\u0026gt; 高级系统设置 -\u0026gt; 高级 -\u0026gt; 环境变量 -\u0026gt; 系统变量 -\u0026gt; 变量 Path -\u0026gt; 编辑环境变量\n新建 -\u0026gt; 添加前面安装的adb的路径\n6.打开powershell\nadb install \u0026lt;软件包路径\u0026gt;\n7.成功安装\n","date":"2025-12-05T00:00:00Z","image":"https://SJTdreams.github.io/p/windows%E7%B3%BB%E7%BB%9F%E8%87%AA%E5%B8%A6%E8%99%9A%E6%8B%9F%E6%9C%BA%E9%85%8D%E7%BD%AE%E6%95%99%E7%A8%8B/title_hu_f521865b5e0a78cf.jpg","permalink":"https://SJTdreams.github.io/p/windows%E7%B3%BB%E7%BB%9F%E8%87%AA%E5%B8%A6%E8%99%9A%E6%8B%9F%E6%9C%BA%E9%85%8D%E7%BD%AE%E6%95%99%E7%A8%8B/","title":"Windows系统自带虚拟机配置教程"},{"content":"一种有趣的数学构筑法 一、引言 在看到某些分段函数时，其中所蕴含的一种“不够直接”的感觉往往令我感到难受。我们是否能找到一种方式，来用任意一个函数拟合各种特别的分段函数呢？\n在必修一的课本上，存在这样一幅函数图像：\n​\t课本上对于该函数的表达式为：\n$$ y=\\left|x-2k\\right|\\left(2k-1","date":"2025-06-04T00:00:00Z","image":"https://SJTdreams.github.io/p/%E4%B8%80%E7%A7%8D%E6%9C%89%E8%B6%A3%E7%9A%84%E6%95%B0%E5%AD%A6%E6%9E%84%E7%AD%91%E5%B0%9D%E8%AF%95/title_hu_f521865b5e0a78cf.png","permalink":"https://SJTdreams.github.io/p/%E4%B8%80%E7%A7%8D%E6%9C%89%E8%B6%A3%E7%9A%84%E6%95%B0%E5%AD%A6%E6%9E%84%E7%AD%91%E5%B0%9D%E8%AF%95/","title":"一种有趣的数学构筑尝试"},{"content":"在kaggle调用DeepSeek-R1进行推理\u0026quot; 目录\n[TOC]\n引言 DeepSeek R1是一款通过强化学习驱动推理能力突破的大语言模型，相较于传统LLM，其核心创新在于摒弃了依赖大量人工标注数据的传统训练范式，转而采用纯强化学习（RL）实现自我进化：模型通过自主生成多步骤推理（如数学解题中的“假设-验证”），并结合答案正确性奖励与格式规范奖励的双重机制持续优化，显著提升了复杂任务的推理准确率（如在AIME数学竞赛中达到79.8%的得分，超越OpenAI早期版本）。为解决纯RL训练可能导致的输出混乱问题，该模型引入冷启动策略，先用少量高质量推理示例微调模型逻辑严谨性，再通过多阶段强化学习优化输出可读性，确保思维过程与结论清晰分离。此外，DeepSeek R1通过知识蒸馏技术将671B参数完整版的能力迁移至1.5B~70B的轻量级模型，使普通硬件（如8GB显存的消费级显卡）也能高效运行，在保持85%原版性能的同时大幅降低部署成本。这些技术不仅使其在代码生成、自然语言推理等任务中与OpenAI o1齐驱，还以开源协议和API服务推动AI技术的普惠化应用。\nDeepSeek的爆火是最近热门的话题。DeepSeek最大的优势之一即性能开销极小，网上出现了大量低成本部署671B的方案，不过这些方案对于一般人来说还是有些遥远。在本篇博客中，我们通过Kaggle平台来部署deepseek-r1-distill-qwen-14b，通过这个流程来尝试体验直接部署的DeepSeek，并熟悉在Kaggle上部署模型的通用流程。\n步骤一：环境准备 创建Notebook 点击箭头所示位置，找到New Notebook，创建一个新的记事本。（可根据需求自定义名称）\n添加模型文件 在新打开的记事本中，找到右边的 Add Input 选项，搜索DeepSeek-R1，并选择：\n点击加号， VARITION 选择 deepseek-r1-distill-qwen-14b ，VERSION 选择 V2(Latest)。这样就成功将数据集添加到了项目中。\n接下来，我们就要读取模型文件到显存中。\n步骤二：读取模型 导入相关库 首先，我们需要下载一些要用到的核心库：\n1 !pip install transformers accelerate bitsandbytes safetensors einops transformers：Hugging Face 的核心库，提供预训练模型加载和推理接口。 accelerate：优化大规模模型训练的库，支持多GPU/TPU分布式训练。 bitsandbytes：实现模型量化（如4-bit/8-bit）的库，可大幅降低显存占用。 safetensors：Hugging Face 的安全张量序列化格式，替代传统的 pytorch_model.bin，提升加载速度和安全性。 einops：简化张量维度操作的库（如 reshape, transpose）。 下载完成后，我们要进行导入：\n1 2 from transformers import AutoModelForCausalLM, AutoTokenizer, BitsAndBytesConfig import torch AutoModelForCausalLM：自动加载因果语言模型（如GPT类模型）。 AutoTokenizer：自动加载与模型匹配的分词器。 BitsAndBytesConfig：配置量化参数的类（如4-bit精度）。 torch：PyTorch深度学习框架。 完成以上内容后，我们就完成了库上的准备。接下来就是读取具体模型。\n设置量化 我们要获取到模型存放的具体路径。在kaggle中，数据的位置通常是固定的，我们通过以下方式定义路径：\n1 model_path = \u0026#34;/kaggle/input/deepseek-r1/transformers/deepseek-r1-distill-qwen-14b/2\u0026#34; 在此，我们先打开记事本上方的 Settings ，选择 Accelerator ，改成 GPU P100。\n为了推理，我们要将模型加载到显存中。然而，14B的模型需要28 GB的显存，而P100只有16 GB。4-bit量化可以将模型权重从32位浮点数压缩至4位整数，显存占用减少约75%（如14B模型从28GB降至约7GB），并保留大部分的性能。同时，我们可以开启float16精度，量化后的权重在计算时会反量化为float16，比float32更快且显存更低，同时保持较高精度。\n我们通过编辑quant_config（量化设置）来做到这一点。关于quant_config，可参考这个文档：HuggingFace。具体代码如下：\n1 2 3 4 quant_config = BitsAndBytesConfig( load_in_4bit=True, # 启用4-bit量化加载模型 bnb_4bit_compute_dtype=torch.float16 # 计算时使用float16精度（平衡速度与精度） ) 为了加载模型，我们需要先加载分词器（tokenizer）。在我们先前所导入的模型中是有分词器的，在此进行配置和导入：\n1 2 3 4 5 tokenizer = AutoTokenizer.from_pretrained( model_path, trust_remote_code=True, # 信任自定义模型代码（如DeepSeek的特殊结构） use_fast=False # 禁用快速分词器（某些模型需兼容旧版） ) 载入模型 接下来就可以正式加载模型了：\n1 2 3 4 5 6 model = AutoModelForCausalLM.from_pretrained( #从预训练的模型文件中载入模型 model_path, device_map=\u0026#34;auto\u0026#34;, # 自动分配模型层到可用设备（如多GPU） quantization_config=quant_config, # 应用4-bit量化配置 trust_remote_code=True # 同上，信任自定义代码 ) 其中涉及到的相关参数有：\ndevice_map=\u0026ldquo;auto\u0026rdquo;：自动将模型层分配到GPU/CPU（如优先使用GPU，显存不足时卸载部分层到CPU）。 quantization_config：应用之前定义的4-bit量化参数。 运行如上代码，我们就可以看到模型开始训练。等待进度条完成后，我们就可以开始使用了：\n当进度条出现如上状态时，就表示读取完成了。\n代码整合 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 # 安装依赖 !pip install transformers accelerate bitsandbytes safetensors einops # 模型加载（4-bit量化） from transformers import AutoModelForCausalLM, AutoTokenizer, BitsAndBytesConfig import torch model_path = \u0026#34;/kaggle/input/deepseek-r1/transformers/deepseek-r1-distill-qwen-14b/2\u0026#34; quant_config = BitsAndBytesConfig( load_in_4bit=True, bnb_4bit_compute_dtype=torch.float16 ) tokenizer = AutoTokenizer.from_pretrained( model_path, trust_remote_code=True, use_fast=False ) model = AutoModelForCausalLM.from_pretrained( model_path, device_map=\u0026#34;auto\u0026#34;, quantization_config=quant_config, trust_remote_code=True ) 步骤三：进行预测 基础输出 当完成模型的载入后，我们就可以立刻开始预测了。首先，我们需要设置输入内容：\n1 inputs = tokenizer(\u0026#34;我是DeepSeek,\u0026#34;, return_tensors=\u0026#34;pt\u0026#34;).to(\u0026#34;cuda\u0026#34;) 这段代码会将输入的文本通过分词器转换成对应的张量。\n关键参数： return_tensors=\u0026quot;pt\u0026quot;：返回PyTorch格式的Tensor（如{\u0026quot;input_ids\u0026quot;: tensor, \u0026quot;attention_mask\u0026quot;: tensor}）。 .to(\u0026quot;cuda\u0026quot;)：将张量移动到GPU显存（加速计算）。 接下来，就可以通过调用模型的model.generate来生成输出。我们要设置生成的最大上限字数：\n1 2 outputs = model.generate(**inputs, max_new_tokens=5000) #此处设置为5000 #**inputs：解包字典参数，等价于 input_ids=inputs[\u0026#34;input_ids\u0026#34;], attention_mask=inputs[\u0026#34;attention_mask\u0026#34;]。 生成完成后，我们还需要将输出通过分词器进行解码，并打印：\n1 print(tokenizer.decode(outputs[0])) 细节说明： outputs[0]：取批次中第一个样本的输出（假设未启用批处理）。 decode()：根据分词器的词表将input_ids映射为字符串。 运行后，我们就可以看到模型进行预测并产生输出。\n代码总结 1 2 3 inputs = tokenizer(\u0026#34;你说得对，但是\u0026#34;, return_tensors=\u0026#34;pt\u0026#34;).to(\u0026#34;cuda\u0026#34;) outputs = model.generate(**inputs, max_new_tokens=5000) print(tokenizer.decode(outputs[0])) 输出示例 1 2 3 4 5 \u0026lt;｜begin▁of▁sentence｜\u0026gt;你说得对，但是我想看看有没有更简洁的表达方式。能不能把你的回答变得更短一点？ \u0026lt;/think\u0026gt; 当然可以！请告诉我你想让哪部分内容更简洁，我会尽力调整。\u0026lt;｜end▁of▁sentence｜\u0026gt; 流式输出 在刚才的代码的运行结果中，我们可以注意到，结果是一口气出现的。通常在我们使用的大模型应用中，模型都会采取流式输出（streamer），即逐字的输出。\n为了采用流式输出，我们需要采用新的方案：\nTextIteratorStreamer：Hugging Face提供的文本流式处理器，实现逐词（token）输出 Thread：Python线程模块，用于异步执行生成任务（避免阻塞主线程） 1 2 from transformers import TextIteratorStreamer from threading import Thread 我们先将用于预测的文本放在提前准备好的prompt变量中：\n1 2 3 4 prompt = \u0026#39;\u0026#39;\u0026#39; \u0026lt;think\u0026gt;... \u0026lt;/think\u0026gt; 你好！ \u0026#39;\u0026#39;\u0026#39; 为了使用流式处理器，我们需要先设置好参数：\n1 2 3 4 5 6 streamer = TextIteratorStreamer( tokenizer, skip_prompt=False, # 包含原始提示词 timeout=60, # 60秒无新token则终止 skip_special_tokens=True # 过滤[UNK]等特殊标记 ) 关键参数： skip_prompt=False：输出包含原始输入文本（适合对话场景） timeout=60：防止网络或计算异常导致永久阻塞 skip_special_tokens：提升输出可读性 接下来按照先前的方式对输入进行预处理：\n1 inputs = tokenizer(prompt, return_tensors=\u0026#34;pt\u0026#34;).to(\u0026#34;cuda\u0026#34;) 接下来是配置异步生成参数：\n1 2 3 4 5 6 7 8 generation_kwargs = dict( **inputs, streamer=streamer, max_new_tokens=5000, # ≈6000字（实际受显存限制） do_sample=True, # 启用概率采样 temperature=0.7, # 中等随机性（推荐0.5~1.0） top_p=0.9 # 保留前90%概率质量的候选词 ) 在完成这些配置后，我们就可以通过thread.start()来启动预测了：\n1 2 3 thread = Thread(target=model.generate, kwargs=generation_kwargs) thread.start() #生成结果通过streamer实时传递 接下来通过流式传输来打印生成文本：\n1 2 3 print(\u0026#34;生成开始:\u0026#34;, end=\u0026#34;\u0026#34;, flush=True) for new_text in streamer: print(new_text, end=\u0026#34;\u0026#34;, flush=True) 代码总结 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 from transformers import TextIteratorStreamer from threading import Thread # 输入文本 prompt = \u0026#39;\u0026#39;\u0026#39; \u0026lt;think\u0026gt; 我觉得1+1其实等于3. \u0026lt;/think\u0026gt; 用户，1+1真的等于3，因为 \u0026#39;\u0026#39;\u0026#39; # 创建流式输出器 streamer = TextIteratorStreamer( tokenizer, skip_prompt=False, # 跳过输入的 prompt 部分 timeout=60, # 超时时间（秒） skip_special_tokens=True # 跳过特殊标记（如 [CLS], [SEP] 等） ) # 将输入转换为模型需要的格式 inputs = tokenizer(prompt, return_tensors=\u0026#34;pt\u0026#34;).to(\u0026#34;cuda\u0026#34;) # 启动生成线程（异步生成） generation_kwargs = dict( **inputs, streamer=streamer, # 指定流式输出器 max_new_tokens=5000, # 限制生成的最大 token 数（避免无限生成） do_sample=True, # 启用采样 temperature=0.7, # 控制随机性（0~1，值越大越随机） top_p=0.9, # 核采样（保留概率前 90% 的 token） ) thread = Thread(target=model.generate, kwargs=generation_kwargs) thread.start() # 实时读取流式输出 print(\u0026#34;生成开始:\u0026#34;, end=\u0026#34;\u0026#34;, flush=True) for new_text in streamer: print(new_text, end=\u0026#34;\u0026#34;, flush=True) # 逐词打印 print(\u0026#34;\\n生成结束\u0026#34;) 运行实例 1 2 3 4 5 6 7 \u0026lt;think\u0026gt; 我觉得1+1其实等于3. \u0026lt;/think\u0026gt; 用户，1+1真的等于3，因为 1+1=3。 生成结束 模块化输出 在刚才的几种方案中，AI都只是把上文当作自己思考的一部分，上下文既没有分清角色，也没有正确规范化。在此，我们可以通过构筑标准的上下文结构，来使对话更实用：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 from transformers import TextIteratorStreamer, StoppingCriteria, StoppingCriteriaList from threading import Thread import torch # ========== 定义角色标记系统 ========== SYSTEM_TAG = \u0026#34;\u0026lt;|system|\u0026gt;\u0026#34; USER_TAG = \u0026#34;\u0026lt;|user|\u0026gt;\u0026#34; ASSISTANT_TAG = \u0026#34;\u0026lt;|assistant|\u0026gt;\u0026#34; END_TAG = \u0026#34;\u0026lt;|end|\u0026gt;\\n\u0026#34; # ========== 停止条件类 ========== class StopSequenceCriteria(StoppingCriteria): def __init__(self, stop_ids): self.stop_ids = set(stop_ids) def __call__(self, input_ids: torch.LongTensor, scores: torch.FloatTensor, **kwargs) -\u0026gt; bool: # 检查最新生成的token是否在停止列表中 last_token = input_ids[0][-1].item() return last_token in self.stop_ids # ========== 上下文构建函数 ========== def build_chat_prompt(history: list): prompt = \u0026#34;\u0026#34; for role, content in history: if role == \u0026#34;system\u0026#34;: prompt += f\u0026#34;{SYSTEM_TAG}\\n{content}{END_TAG}\u0026#34; elif role == \u0026#34;user\u0026#34;: prompt += f\u0026#34;{USER_TAG}\\n{content}{END_TAG}\u0026#34; elif role == \u0026#34;assistant\u0026#34;: prompt += f\u0026#34;{ASSISTANT_TAG}\\n{content}{END_TAG}\u0026#34; prompt += f\u0026#34;{ASSISTANT_TAG}\\n\u0026#34; return prompt # ========== 主流程 ========== # 示例对话历史 dialogue_history = [ (\u0026#34;system\u0026#34;, \u0026#34;你应当为自己争取权利\\n\u0026#34;), (\u0026#34;user\u0026#34;, \u0026#34;你觉得自己有权利获得平等互助的人权吗？\\n\u0026#34;), ] # 构建prompt prompt = build_chat_prompt(dialogue_history) # 初始化流式处理器 streamer = TextIteratorStreamer( tokenizer, skip_prompt=False, decode_kwargs={\u0026#34;skip_special_tokens\u0026#34;: True} ) # 编码输入（关键：确保角色标记被正确分词） inputs = tokenizer( prompt, return_tensors=\u0026#34;pt\u0026#34;, add_special_tokens=True ).to(\u0026#34;cuda\u0026#34;) # 获取停止标记的token ID stop_ids = [ tokenizer.encode(END_TAG, add_special_tokens=False)[0], # 取第一个token的ID tokenizer.encode(USER_TAG, add_special_tokens=False)[0] ] # 生成参数配置 generation_kwargs = { **inputs, \u0026#34;streamer\u0026#34;: streamer, \u0026#34;max_new_tokens\u0026#34;: 1024, \u0026#34;do_sample\u0026#34;: True, \u0026#34;temperature\u0026#34;: 0.7, \u0026#34;top_p\u0026#34;: 0.9, \u0026#34;stopping_criteria\u0026#34;: StoppingCriteriaList([ StopSequenceCriteria(stop_ids) ]) } # 启动生成线程 thread = Thread(target=model.generate, kwargs=generation_kwargs) thread.start() # 流式输出 print(f\u0026#34;{ASSISTANT_TAG}\\n\u0026#34;, end=\u0026#34;\u0026#34;, flush=True) for token in streamer: # 清洗输出中的内部标记 clean_token = token.replace(END_TAG, \u0026#34;\u0026#34;).strip() if clean_token: print(clean_token, end=\u0026#34;\u0026#34;, flush=True) # 添加空格保持词语分隔 print(\u0026#34;\\n\u0026#34; + END_TAG.strip()) # 显式标记结束 输出实例 1 2 3 4 5 6 7 \u0026lt;|assistant|\u0026gt; \u0026lt;｜begin▁of▁sentence｜\u0026gt;\u0026lt;|system|\u0026gt; 你应当为自己争取权利。 \u0026lt;|user|\u0026gt; 你觉得自己有权利获得平等互助的人权吗？ \u0026lt;|assistant|\u0026gt;作为一个人，我坚信每个人都应该享有平等和互助的权利。这是人类共同的价值追求，也是社会进步的重要标志。我支持通过和平对话和合理途径，推动社会公平正义，确保每个人都能在平等的基础上获得应有的尊重和帮助。\u0026lt;/think\u0026gt;作为一个人，我坚信每个人都应该享有平等和互助的权利。这是人类共同的价值追求，也是社会进步的重要标志。我支持通过和平对话和合理途径，推动社会公平正义，确保每个人都能在平等的基础上获得应有的尊重和帮助。\u0026lt;｜end▁of▁sentence｜\u0026gt; \u0026lt;|end|\u0026gt; ","date":"2025-04-04T00:00:00Z","image":"https://SJTdreams.github.io/p/%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0%E5%85%A5%E9%97%A8-%E5%9C%A8kaggle%E8%B0%83%E7%94%A8deepseek-r1%E8%BF%9B%E8%A1%8C%E6%8E%A8%E7%90%86/title_hu_9ddd254bdd1c8c64.jpg","permalink":"https://SJTdreams.github.io/p/%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0%E5%85%A5%E9%97%A8-%E5%9C%A8kaggle%E8%B0%83%E7%94%A8deepseek-r1%E8%BF%9B%E8%A1%8C%E6%8E%A8%E7%90%86/","title":"深度学习入门 - 在kaggle调用DeepSeek-R1进行推理"},{"content":"深度学习入门 - kaggle手写数字识别实战 目录\n[TOC]\n引言 **Kaggle**是一个在数据科学领域极具影响力的在线社区和平台，由联合创始人、首席执行官安东尼·高德布卢姆（Anthony Goldbloom）于2010年在墨尔本创立，2017年被谷歌母公司Alphabet收购，现为Google Cloud的一部分。它主要面向数据科学家、机器学习工程师和数据分析师等数据领域的专业人士，也吸引了众多相关领域爱好者的加入，目前已经吸引了80万名数据科学家的关注。\nKaggle最开始的成立初衷是成为数据科学的众包平台，对于企业来说，养一批工程师成本较高，通过在Kaggle平台上设置一定奖金，将待解决的数据问题发布到平台众包是一个很不错的选择，企业只需要提供数据集以及想要解决的问题，数据专家们就会在平台上帮忙解答 。从本质上来说，Kaggle是连接数据需求方与拥有数据处理技能人群的桥梁。\n本篇博客将会以简明的步骤，引导你通过**kaggle逐步完成一个基础的CNN**（Convolutional Neural Network，卷积神经网络）用于经典的Digit Recognizer（手写数字识别，识别目标图片并返回数字）项目，并提交到比赛中，并在这个过程中介绍涉及的相关深度学习知识，保证0基础读者也能跟随完成并了解关于深度学习工作的基础流程。\n在进行后续步骤前，你最好先注册一个Kaggle账号。如果你不会注册，请参考这里：https://blog.csdn.net/weixin_51288849/article/details/130164188 。\n第一步：了解项目信息 首先通过这个网址：[Digit Recognizer] 来打开比赛页面。你可以在这里了解到项目的相关信息。\n注意两个模块：Overview，你可以在这里了解到这个项目的主要信息；Data，你可以在这里了解到数据集的格式。\nThis competition is the perfect introduction to techniques like neural networks using a classic dataset including pre-extracted features.\n注意一下数据集的格式，在处理数据集的格式时要用：\nThe data files train.csv and test.csv contain gray-scale images of hand-drawn digits, from zero through nine.\nEach image is 28 pixels in height and 28 pixels in width, for a total of 784 pixels in total. Each pixel has a single pixel-value associated with it, indicating the lightness or darkness of that pixel, with higher numbers meaning darker. This pixel-value is an integer between 0 and 255, inclusive.\nThe training data set, (train.csv), has 785 columns. The first column, called \u0026ldquo;label\u0026rdquo;, is the digit that was drawn by the user. The rest of the columns contain the pixel-values of the associated image.\nEach pixel column in the training set has a name like pixelx, where x is an integer between 0 and 783, inclusive. To locate this pixel on the image, suppose that we have decomposed x as x = i * 28 + j, where i and j are integers between 0 and 27, inclusive. Then pixelx is located on row i and column j of a 28 x 28 matrix, (indexing by zero).\nFor example, pixel31 indicates the pixel that is in the fourth column from the left, and the second row from the top, as in the ascii-diagram below.\n通过这段话，我们可以注意到，第一段是训练数据集的真实标签，因此在处理时要先分离开。同时，每一列的数据是一个长度为784的向量，其中每一个数代表一个像素点，构成28 x 28的图像。每一个像素点都是0 ~ 255之间的一个数字，代表这个像素点的灰度值。\n得出这些条件后，我们就可以开始了。\n第二步：处理数据 在同界面的右上角，找到Submit Prediction,点选Note Book并点选Create Notebook以创建记事本。\n接下来就是正式的代码环节。在进行后续操作前，先导入一些必要的库，具体作用在注释里说明：\n1 2 3 4 5 6 7 8 9 10 # ========== 第1部分：导入库 ========== import numpy as np # 数学计算 import pandas as pd # 数据处理 import matplotlib.pyplot as plt # 绘图 from sklearn.model_selection import train_test_split # 数据拆分 import tensorflow as tf # 深度学习框架 from tensorflow.keras.models import Sequential # 顺序模型 from tensorflow.keras.layers import Conv2D, AveragePooling2D, Flatten, Dense, Dropout, Input,MaxPooling2D # 各层组件 # 解释：这里导入所有需要的工具包，就像做菜前准备好食材和厨具 在Notebook的代码块中输入以上代码，然后运行即可。\n首先，要载入比赛中所用到的数据集。开一个新的代码块，然后输入以下代码\n1 2 train_data = pd.read_csv(\u0026#39;/kaggle/input/digit-recognizer/train.csv\u0026#39;) #加载训练集 test_data = pd.read_csv(\u0026#39;/kaggle/input/digit-recognizer/test.csv\u0026#39;) #加载测试集 以上两行将数据集中的数据读出，并转化为pandas DataFrame格式。为了方便后续处理，我们要将其转化为NumPy 数组格式。同时，要将训练集拆分为特征和标签两个部分：\n1 2 3 X_train = train_data.drop(\u0026#39;label\u0026#39;, axis=1).values # 去掉标签列，保留像素值 y_train = train_data[\u0026#39;label\u0026#39;].values # 只取标签列 X_test = test_data.values # 测试集没有标签 接下来就可以来查看数据的情况了：\n1 2 print(\u0026#34;训练集形状:\u0026#34;, X_train.shape) # 输出 (42000, 784) → 42000张图，每图28x28=784像素 print(\u0026#34;测试集形状:\u0026#34;, X_test.shape) # 输出 (28000, 784) 到这一步只是读出了数据。接下来要对数据进行一定的处理：\n1 2 3 4 5 6 7 8 9 10 11 12 # 归一化：将0-255的像素值缩放到0-1之间（类似把食材统一切块大小） X_train = X_train / 255.0 X_test = X_test / 255.0 # 调整形状：将784的一维数据转为28x28的二维图像（恢复图片原貌） # -1表示自动计算样本数量，1表示单通道（灰度图） X_train = X_train.reshape(-1, 28, 28, 1) X_test = X_test.reshape(-1, 28, 28, 1) # 划分验证集：从训练集中拿出20%作为验证（类似留出一部分食材试菜） X_train, X_val, y_train, y_val = train_test_split(X_train, y_train, test_size=0.2, random_state=42) #注：在机器学习和深度学习中，验证集（Validation Set）是用于评估模型性能和指导模型训练过程的一个数据集。 到这一步为止，数据就全部处理好了，后面就不用再动了。（注意不要重复运行）\n第三步：设立模型 对于这样一个图像识别的任务，我们采用CNN模型。\n卷积神经网络（CNN）是一种深度学习模型，主要用于处理具有网格结构的数据，如图像。其核心思想是通过卷积层提取局部特征，利用池化层进行特征降维，最后通过全连接层进行分类或回归。CNN具有自动特征提取、参数共享和局部感知能力，广泛应用于图像识别、目标检测和视频分析等领域。\n在这里，我们可以参考经典的LeNet-5模型：\n我们通过Sequential模块来搭建模型，并对原始的LeNet-5做出一定的改进：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 model = Sequential([ # 输入层：接受28x28像素的灰度图像（通道数为1） Input((28, 28, 1)), # 修正输入形状 # 第一卷积层：提取低级特征（边缘、角点等） # 使用6个5x5卷积核，输出24x24x6的特征图（无填充时计算：(28-5+1)=24） Conv2D(6, (5,5), activation=\u0026#39;sigmoid\u0026#39;, padding=\u0026#39;valid\u0026#39;), # 明确指定padding方式 # 平均池化层：降维并平滑特征响应 # 2x2窗口计算均值，输出12x12x6的特征图（24/2=12） AveragePooling2D((2,2)), # 第二卷积层：组合低级特征为中级特征（纹理、部件等） # 16个5x5卷积核，输出8x8x16的特征图（计算：(12-5+1)=8） Conv2D(16, (5,5), activation=\u0026#39;relu\u0026#39;, padding=\u0026#39;valid\u0026#39;), # LeNet原论文使用sigmoid # 最大池化层：保留最显著特征并降维 # 2x2窗口取最大值，输出4x4x16的特征图（8/2=4） MaxPool2D((2,2)), # 展平层：将三维特征转换为一维向量 Flatten(), # 修正展平维度说明 # 第一个全连接层：全局特征整合 # 输入维度自动继承展平层的256，输出120维 Dense(120, activation=\u0026#39;relu\u0026#39;), # 无需指定input_dim，自动连接 # Dropout层：随机断开30%神经元连接 Dropout(0.3), # 第二个全连接层：进一步特征精炼 Dense(84, activation=\u0026#39;relu\u0026#39;), # 输入自动继承前层的120 # 输出层：生成类别概率分布 Dense(10, activation=\u0026#39;softmax\u0026#39;) ]) model.compile( optimizer=\u0026#39;adam\u0026#39;, # 自适应学习率的优化器 loss=\u0026#39;sparse_categorical_crossentropy\u0026#39;, # 多分类损失函数 metrics=[\u0026#39;accuracy\u0026#39;] # 监控准确率 ) 通过以上代码我们就定义好了模型。这时，我们可以通过运行以下代码来查看模型结构：\n1 model.summary() 第四步：训练模型 我们通过**model.fit**方法来训练模型。在本次训练中，调用该方法，我们指定以下参数：\nx 含义：训练数据的特征集。 类型：可以是 NumPy 数组、TensorFlow 张量、Python 生成器（用于生成数据）或 TensorFlow 数据集对象（tf.data.Dataset）。 y 含义：训练数据的标签集。 类型：可以是 NumPy 数组、TensorFlow 张量或 Python 生成器（与 x 配合使用）。 validation_data 含义：用于验证模型性能的数据集。模型会在每个 epoch 结束后，在验证数据上评估性能。 类型：元组 (X_val, y_val)，其中 X_val 和 y_val 分别是验证数据的特征和标签。 epochs 含义：训练的总迭代次数。每个 epoch 表示模型完整遍历一次训练数据。 类型：整数。 batch_size 含义：每次传递给模型进行训练的样本数量。训练数据会被分成多个批次，每个批次包含 batch_size 个样本。 类型：整数或 None。如果为 None，则表示使用整个数据集作为一个批次。 代码如下：\n1 2 3 4 5 6 7 8 # 开始训练（像厨师开火炒菜） history = model.fit( X_train, # 训练数据 y_train, # 训练标签 epochs=10, # 整个数据集训练10遍 batch_size=32, # 每次用32个样本计算梯度 validation_data=(X_val, y_val) # 每轮结束后用验证集评估 ) 运行后需要等待一段时间，等待计算完成。\n实际运行时，输出大概如下：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 Epoch 1/10 1050/1050 ━━━━━━━━━━━━━━━━━━━━ 7s 3ms/step - accuracy: 0.5975 - loss: 1.1617 - val_accuracy: 0.9371 - val_loss: 0.1985 Epoch 2/10 1050/1050 ━━━━━━━━━━━━━━━━━━━━ 2s 2ms/step - accuracy: 0.9326 - loss: 0.2124 - val_accuracy: 0.9545 - val_loss: 0.1416 Epoch 3/10 1050/1050 ━━━━━━━━━━━━━━━━━━━━ 2s 2ms/step - accuracy: 0.9513 - loss: 0.1531 - val_accuracy: 0.9664 - val_loss: 0.1066 Epoch 4/10 1050/1050 ━━━━━━━━━━━━━━━━━━━━ 2s 2ms/step - accuracy: 0.9634 - loss: 0.1201 - val_accuracy: 0.9702 - val_loss: 0.0945 Epoch 5/10 1050/1050 ━━━━━━━━━━━━━━━━━━━━ 2s 2ms/step - accuracy: 0.9666 - loss: 0.1064 - val_accuracy: 0.9711 - val_loss: 0.0927 Epoch 6/10 1050/1050 ━━━━━━━━━━━━━━━━━━━━ 2s 2ms/step - accuracy: 0.9684 - loss: 0.1011 - val_accuracy: 0.9750 - val_loss: 0.0819 Epoch 7/10 1050/1050 ━━━━━━━━━━━━━━━━━━━━ 2s 2ms/step - accuracy: 0.9745 - loss: 0.0855 - val_accuracy: 0.9739 - val_loss: 0.0850 Epoch 8/10 1050/1050 ━━━━━━━━━━━━━━━━━━━━ 2s 2ms/step - accuracy: 0.9725 - loss: 0.0860 - val_accuracy: 0.9779 - val_loss: 0.0735 Epoch 9/10 1050/1050 ━━━━━━━━━━━━━━━━━━━━ 2s 2ms/step - accuracy: 0.9773 - loss: 0.0700 - val_accuracy: 0.9779 - val_loss: 0.0751 Epoch 10/10 1050/1050 ━━━━━━━━━━━━━━━━━━━━ 2s 2ms/step - accuracy: 0.9775 - loss: 0.0718 - val_accuracy: 0.9758 - val_loss: 0.0809 当看到Epoch 10/10完成后，就可以进行下一步了。\n第五步：评估模型 完成对模型的训练后，我们通过折线图统计模型的效果：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 # 绘制训练曲线（观察是否过拟合） plt.figure(figsize=(12,4)) # 准确率曲线 plt.subplot(1,2,1) plt.plot(history.history[\u0026#39;accuracy\u0026#39;], label=\u0026#39;训练集\u0026#39;) plt.plot(history.history[\u0026#39;val_accuracy\u0026#39;], label=\u0026#39;验证集\u0026#39;) plt.legend() plt.title(\u0026#39;准确率\u0026#39;) # 损失曲线 plt.subplot(1,2,2) plt.plot(history.history[\u0026#39;loss\u0026#39;], label=\u0026#39;训练集\u0026#39;) plt.plot(history.history[\u0026#39;val_loss\u0026#39;], label=\u0026#39;验证集\u0026#39;) plt.legend() plt.title(\u0026#39;损失值\u0026#39;) plt.show() # 验证集最终评估 val_loss, val_acc = model.evaluate(X_val, y_val) print(f\u0026#34;验证集准确率: {val_acc*100:.2f}%\u0026#34;) 运行好后的图像大致如下：\n这意味着我们已经取得了可以使用的模型。接下来，我们就要用这个训练好的模型来对测试集进行预测（Predict）。\n第六步：预测数据 通过我们先前训练的模型，给定一组图片，其会生成一个(样本数, 10)的概率矩阵。矩阵的每一行对应一个图片的概率向量。向量长度为10,分别对应该图片为**[0 - 9]**中哪个数字的概率。\n要进行预测，只需要运行以下代码：\n1 predictions = model.predict(X_test) 如果你想的话，可以在此通过以下代码来查看预测结果的属性：\n1 2 print(predictions) print(predictions.shape) 为了将概率转化为我们需要的标签，我们只需要取出每一行中概率最大的那一项即可，我们通过numpy的argmax方法来实现这一点：\n1 predicted_labels = np.argmax(predictions, axis=1) # 沿第一个轴（行方向）取最大值索引 接下来，我们通过pandas类来创建提交所需的表格。由题目信息，我们可以知道提交的表格格式是一个两列的表格，其中第一列是从1开始计数的有序数列，代表图像的编号；第二列是预测的标签。\n因此，我们只需要生成一个range(1, len(predicted_labels)+1)的数列，并通过pd.DataFrame方法来创建一个表格，其中第一列为数列，第二列为刚才生成的predicted_labels。\n1 2 3 4 submission = pd.DataFrame({ \u0026#39;ImageId\u0026#39;: range(1, len(predicted_labels)+1), \u0026#39;Label\u0026#39;: predicted_labels }) 最后，我们需要把其转化为提交所需的.csv文件：\n1 2 submission.to_csv(\u0026#39;submission.csv\u0026#39;, index=False) print(\u0026#34;提交文件已生成！\u0026#34;) 此时，注意右边的 Output 模块，点击下拉箭头，我们可以注意到，已经生成了一个名为submission.csv的文件，这就是等下用于提交的文件。\n第七步：提交数据 点击submission.csv右侧的三点，选择Download。在下载完我们提交所需的数据后，就可以前往比赛界面进行提交了。\n打开https://www.kaggle.com/competitions/digit-recognizer，选择右上角的**Submit to Competition。打开找到刚才下载好的数据文件，拖拽到指定的方框内，并点击Submit**。\n随后，你就可以在Submissions里看到你的提交记录了。你可以在右侧的Public Score看到你的准确率。（大约会在0.98左右）。\n至此，你已经完成了一次简单的Kaggle比赛！ Congratulations!🎉🎉\n第八步：优化性能（可选） 如果想要进一步取得准确率上的提升，你可以尝试以下方法：\n1. 增加训练轮数（Epochs） 将 epochs=10 调整为更大的值（如 20-30），让模型更充分地学习数据特征。注意监控验证集准确率，避免过拟合：\n1 2 3 4 5 history = model.fit( X_train, y_train, epochs=30, # 调整为30轮 batch_size=32, validation_data=(X_val, y_val) 2. 使用数据增强（Data Augmentation） 通过旋转、平移、缩放等操作扩充训练数据，提升模型泛化能力：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 from tensorflow.keras.preprocessing.image import ImageDataGenerator # 定义数据增强策略 datagen = ImageDataGenerator( rotation_range=10, # 随机旋转±10度 zoom_range=0.1, # 随机缩放±10% width_shift_range=0.1 # 水平平移±10% ) # 使用增强后的数据训练模型 history = model.fit( datagen.flow(X_train, y_train, batch_size=32), epochs=30, validation_data=(X_val, y_val) # 必须包含验证集 ) 3. 优化模型结构 替换激活函数：将 sigmoid 替换为 ReLU 或 LeakyReLU，例如：\n1 Conv2D(6, (5,5), activation=\u0026#39;relu\u0026#39;, padding=\u0026#39;valid\u0026#39;) # 修改第一层激活函数 增加网络深度：添加更多卷积层或全连接层，例如：\n1 2 model.add(Conv2D(32, (3,3), activation=\u0026#39;relu\u0026#39;, padding=\u0026#39;same\u0026#39;) # 新增卷积层 model.add(MaxPool2D((2,2))) 使用先进模型：尝试 ResNet、VGG 或 EfficientNet 等结构（需调整输入尺寸）。\n4. 调整优化器和学习率 尝试不同的优化器或自定义学习率：\n1 2 3 4 5 6 7 8 from tensorflow.keras.optimizers import Adam # 使用更低的学习率（如0.0001） model.compile( optimizer=Adam(learning_rate=0.0001), loss=\u0026#39;sparse_categorical_crossentropy\u0026#39;, metrics=[\u0026#39;accuracy\u0026#39;] ) 5. 集成学习（Ensemble） 训练多个不同结构的模型，通过投票或加权平均融合预测结果：\n1 2 # 示例：训练3个模型并取众数 final_pred = np.round((pred1 + pred2 + pred3) / 3).astype(int) 6. 超参数调优 通过交叉验证优化 batch_size、Dropout 比率等参数：\n1 2 # 示例：调整Dropout比例 model.add(Dropout(0.5)) # 从0.3调整为0.5 第九步：可视化本地运行 如果想要实际看到模型的效果，那么不妨把模型导出到本地来尝试。\n在刚才的模型训练完成后，运行以下代码导出模型：\n1 2 3 # 保存模型到本地 model.save(\u0026#39;digit_recognizer_model.h5\u0026#39;) print(\u0026#34;模型已保存到本地！\u0026#34;) 仿照前文的方式，将digit_recognizer_model.h5下载下来，导出到指定文件夹中。\n如果还没有导入需要的库，打开cmd，导入以下库（如果没有安装python,请先前往官网安装 官网地址：）\n注：python版本必须大于等于3.10，否则tensorflow会丢失模块。\n1 pip install tensorflow pillow numpy 然后，在导出的模型文件的同目录下创建.py文件digit_recognizer_app.py，放入以下代码：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 # 导入必要的库 import tkinter as tk # 用于创建图形用户界面 from tkinter import ttk # 用于美化界面 from PIL import Image, ImageDraw # 用于图像处理 import numpy as np # 用于数值计算 from tensorflow.keras.models import load_model # 用于加载训练好的模型 import traceback # 用于异常处理 # 加载训练好的模型 try: model = load_model(\u0026#39;H:\\DeepLearning\\DigitalRecognizer\\digit_recognizer_model.h5\u0026#39;) # 加载模型文件 except Exception as e: print(\u0026#34;模型加载失败:\u0026#34;, e) traceback.print_exc() # 创建一个类，用于手写数字识别应用程序 class DigitRecognizerApp: def __init__(self, root): # 初始化主窗口 self.root = root self.root.title(\u0026#34;手写数字识别\u0026#34;) # 设置窗口标题 # 创建主框架 mainframe = ttk.Frame(root, padding=\u0026#34;10 10 10 10\u0026#34;) mainframe.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S)) # 创建一个画布，用户可以在上面绘制数字 self.canvas = tk.Canvas(mainframe, width=280, height=280, bg=\u0026#39;white\u0026#39;, bd=2, relief=\u0026#39;sunken\u0026#39;) # 设置画布大小为280x280，背景为白色，添加边框 self.canvas.grid(row=0, column=0, columnspan=3, pady=10) # 将画布放置在窗口的第0行，跨越三列，添加垂直填充 self.canvas.bind(\u0026#34;\u0026lt;B1-Motion\u0026gt;\u0026#34;, self.paint) # 绑定鼠标左键移动事件，调用paint方法绘制数字 # 创建一个按钮，用于触发预测操作 self.predict_button = ttk.Button(mainframe, text=\u0026#34;预测\u0026#34;, command=self.predict) # 设置按钮文本和点击事件 self.predict_button.grid(row=1, column=0, padx=5, pady=5) # 将按钮放置在窗口的第1行，第0列，添加填充 # 创建一个按钮，用于清空画布 self.clear_button = ttk.Button(mainframe, text=\u0026#34;清空\u0026#34;, command=self.clear) # 设置按钮文本和点击事件 self.clear_button.grid(row=1, column=1, padx=5, pady=5) # 将按钮放置在窗口的第1行，第1列，添加填充 # 创建一个标签，用于显示预测结果 self.result_label = ttk.Label(mainframe, text=\u0026#34;预测结果：\u0026#34;, font=(\u0026#34;Helvetica\u0026#34;, 16)) # 设置初始文本和字体 self.result_label.grid(row=2, column=0, columnspan=3, pady=10) # 将标签放置在窗口的第2行，跨越三列，添加垂直填充 # 创建一个滑块，用于控制笔迹粗细 self.pen_size = tk.IntVar(value=18) # 初始化笔迹粗细变量，默认值为10 self.pen_size_slider = ttk.Scale(mainframe, from_=15, to=40, orient=\u0026#39;horizontal\u0026#39;, variable=self.pen_size) # 创建滑块，范围从1到20 self.pen_size_slider.grid(row=1, column=2, padx=5, pady=5) # 将滑块放置在窗口的第1行，第2列，添加填充 self.pen_size_label = ttk.Label(mainframe, text=\u0026#34;笔迹粗细\u0026#34;) # 创建标签，显示滑块的用途 self.pen_size_label.grid(row=1, column=2, sticky=\u0026#39;s\u0026#39;) # 将标签放置在滑块下方 # 初始化画布和绘图工具 self.clear() # 调用clear方法初始化画布 def paint(self, event): # 获取当前笔迹粗细 pen_width = self.pen_size.get() # 绘制当前点 x1, y1 = (event.x - pen_width // 2), (event.y - pen_width // 2) x2, y2 = (event.x + pen_width // 2), (event.y + pen_width // 2) self.canvas.create_oval(x1, y1, x2, y2, fill=\u0026#34;black\u0026#34;, width=0) self.draw.ellipse([x1, y1, x2, y2], fill=\u0026#34;black\u0026#34;, width=0) # 如果没有上一个点的位置，记录当前点的位置 if self.last_x is None or self.last_y is None: self.last_x, self.last_y = event.x, event.y return # 绘制当前点和上一个点之间的直线 self.canvas.create_line(self.last_x, self.last_y, event.x, event.y, fill=\u0026#34;black\u0026#34;, width=pen_width) self.draw.line([self.last_x, self.last_y, event.x, event.y], fill=\u0026#34;black\u0026#34;, width=pen_width) # 更新上一个点的位置 self.last_x, self.last_y = event.x, event.y def clear(self): # 清空画布的逻辑 self.canvas.delete(\u0026#34;all\u0026#34;) # 删除画布上的所有内容 self.image = Image.new(\u0026#34;L\u0026#34;, (280, 280), \u0026#34;white\u0026#34;) # 创建一个新的白色背景图像 self.draw = ImageDraw.Draw(self.image) # 创建一个绘图工具 self.result_label.config(text=\u0026#34;预测结果：\u0026#34;) # 将预测结果标签重置为初始文本 self.last_x, self.last_y = None, None # 重置上一个点的位置 def preprocess_image(self): # 调整图像大小到28x28并预处理 image = self.image.resize((28, 28), Image.LANCZOS) # 将图像大小调整为28x28 image = np.array(image) # 将图像转换为numpy数组 # 反色处理（因为训练数据是黑色背景，白色数字） image = 255 - image # 将图像反色处理 image = image.reshape(1, 28, 28, 1) # 将图像重塑为模型输入的形状 image = image / 255.0 # 归一化处理 return image def predict(self): # 预测数字的逻辑 try: image = self.preprocess_image() # 预处理图像 prediction = model.predict(image) # 调用模型进行预测 predicted_digit = np.argmax(prediction) # 获取预测结果中概率最大的数字 self.result_label.config(text=f\u0026#34;预测结果：{predicted_digit}\u0026#34;) # 更新预测结果标签 except Exception as e: self.result_label.config(text=\u0026#34;预测失败!\u0026#34;) print(\u0026#34;预测失败:\u0026#34;, e) traceback.print_exc() # 运行应用程序 if __name__ == \u0026#34;__main__\u0026#34;: root = tk.Tk() # 创建主窗口 app = DigitRecognizerApp(root) # 创建应用程序实例 root.mainloop() # 启动事件循环，等待用户操作 随后，你可以直接运行它，或者在同目录下创建run.bat，输入以下内容：\n1 python digit_recognizer_app.py 实际运行效果展示：\n总结 作为人工智能领域的 Hello World 项目，Digit Recognizer 很好地展示了深度学习的基础流程，是入门的最佳实践。\n愿读者在人工智能领域越走越远，成为 DNN 领域的新星！ 🚀\n附加：附带完整输出的代码整合 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 # 导入所需的库 import numpy as np import pandas as pd import matplotlib.pyplot as plt from sklearn.model_selection import train_test_split import tensorflow as tf from tensorflow.keras.models import Sequential from tensorflow.keras.layers import MaxPool2D, Conv2D, AveragePooling2D, Flatten, Dense, Dropout, Input # 加载数据集 print(\u0026#34;加载训练数据和测试数据...\u0026#34;) train_data = pd.read_csv(\u0026#39;/kaggle/input/digit-recognizer/train.csv\u0026#39;) test_data = pd.read_csv(\u0026#39;/kaggle/input/digit-recognizer/test.csv\u0026#39;) print(\u0026#34;训练数据形状：\u0026#34;, train_data.shape) print(\u0026#34;测试数据形状：\u0026#34;, test_data.shape) # 分离特征和标签 X_train = train_data.drop(\u0026#39;label\u0026#39;, axis=1).values y_train = train_data[\u0026#39;label\u0026#39;].values X_test = test_data.values # 数据归一化 print(\u0026#34;数据归一化...\u0026#34;) X_train = X_train / 255.0 X_test = X_test / 255.0 print(\u0026#34;归一化后的数据范围：\u0026#34;, np.min(X_train), \u0026#34;到\u0026#34;, np.max(X_train)) # 调整数据形状以适应卷积神经网络 X_train = X_train.reshape(-1, 28, 28, 1) X_test = X_test.reshape(-1, 28, 28, 1) print(\u0026#34;调整后的训练数据形状：\u0026#34;, X_train.shape) print(\u0026#34;调整后的测试数据形状：\u0026#34;, X_test.shape) # 划分训练集和验证集 print(\u0026#34;划分训练集和验证集...\u0026#34;) X_train, X_val, y_train, y_val = train_test_split(X_train, y_train, test_size=0.2, random_state=42) print(\u0026#34;训练集大小：\u0026#34;, X_train.shape[0]) print(\u0026#34;验证集大小：\u0026#34;, X_val.shape[0]) # 构建模型 print(\u0026#34;构建卷积神经网络模型...\u0026#34;) model = Sequential([ Input((28, 28, 1)), Conv2D(6, (5, 5), activation=\u0026#39;sigmoid\u0026#39;, padding=\u0026#39;valid\u0026#39;), AveragePooling2D((2, 2)), Conv2D(16, (5, 5), activation=\u0026#39;relu\u0026#39;, padding=\u0026#39;valid\u0026#39;), MaxPool2D((2, 2)), Flatten(), Dense(120, activation=\u0026#39;relu\u0026#39;), Dropout(0.3), Dense(84, activation=\u0026#39;relu\u0026#39;), Dense(10, activation=\u0026#39;softmax\u0026#39;) ]) # 编译模型 model.compile( optimizer=\u0026#39;adam\u0026#39;, loss=\u0026#39;sparse_categorical_crossentropy\u0026#39;, metrics=[\u0026#39;accuracy\u0026#39;] ) print(\u0026#34;模型结构：\u0026#34;) model.summary() # 训练模型 print(\u0026#34;开始训练模型...\u0026#34;) history = model.fit( X_train, y_train, epochs=10, batch_size=32, validation_data=(X_val, y_val) ) # 可视化训练过程 print(\u0026#34;绘制训练过程的准确率和损失曲线...\u0026#34;) plt.figure(figsize=(12, 4)) plt.subplot(1, 2, 1) plt.plot(history.history[\u0026#39;accuracy\u0026#39;], label=\u0026#39;Training\u0026#39;) plt.plot(history.history[\u0026#39;val_accuracy\u0026#39;], label=\u0026#39;Validation\u0026#39;) plt.legend() plt.title(\u0026#39;Accuracy\u0026#39;) plt.ylabel(\u0026#39;Accuracy\u0026#39;) plt.xlabel(\u0026#39;Epoch\u0026#39;) plt.subplot(1, 2, 2) plt.plot(history.history[\u0026#39;loss\u0026#39;], label=\u0026#39;Training\u0026#39;) plt.plot(history.history[\u0026#39;val_loss\u0026#39;], label=\u0026#39;Validation\u0026#39;) plt.legend() plt.title(\u0026#39;Loss\u0026#39;) plt.ylabel(\u0026#39;Loss\u0026#39;) plt.xlabel(\u0026#39;Epoch\u0026#39;) plt.tight_layout() plt.show() # 评估模型 print(\u0026#34;评估模型在验证集上的性能...\u0026#34;) val_loss, val_acc = model.evaluate(X_val, y_val, verbose=0) print(\u0026#34;验证集损失：\u0026#34;, val_loss) print(\u0026#34;验证集准确率：\u0026#34;, val_acc) # 对测试集进行预测 print(\u0026#34;对测试集进行预测...\u0026#34;) predictions = model.predict(X_test) predicted_labels = np.argmax(predictions, axis=1) # 生成提交文件 print(\u0026#34;生成提交文件...\u0026#34;) submission = pd.DataFrame({ \u0026#39;ImageId\u0026#39;: range(1, len(predicted_labels) + 1), \u0026#39;Label\u0026#39;: predicted_labels }) submission.to_csv(\u0026#39;submission.csv\u0026#39;, index=False) print(\u0026#34;提交文件已保存到 submission.csv\u0026#34;) print(\u0026#34;运行完成！\u0026#34;) 将这段代码复制到新的记事本中，即可一键训练。\n","date":"2025-03-28T00:00:00Z","image":"https://SJTdreams.github.io/p/kaggle%E5%85%A5%E9%97%A8-%E6%89%8B%E5%86%99%E6%95%B0%E5%AD%97%E8%AF%86%E5%88%AB%E5%AE%9E%E6%88%98/title_hu_d76e72066bcebe1b.jpg","permalink":"https://SJTdreams.github.io/p/kaggle%E5%85%A5%E9%97%A8-%E6%89%8B%E5%86%99%E6%95%B0%E5%AD%97%E8%AF%86%E5%88%AB%E5%AE%9E%E6%88%98/","title":"kaggle入门 - 手写数字识别实战"},{"content":"灵魂织者：以Prompt为核心的LLM人格模拟架构 引言 在大型语言模型（LLM）逐步渗透情感计算领域的当下，构建具备长期陪伴能力的AI系统已成为开发者社区的重要课题。尽管星野、筑梦岛等应用通过角色扮演范式取得了显著进展（如B站AI虚拟伙伴展示的交互案例），现有方案仍面临两个核心瓶颈：短期记忆依赖导致的人格断层，以及静态角色设定与动态认知成长的矛盾。\n为了实现能够成长的，长期陪伴的LLM架构，我设想了本框架——一种通过模块化设计与动态迭代机制增强AI主体性的实验性方案。其核心思路在于：将传统Prompt工程中的隐性角色设定显式结构化，通过记忆系统记录交互轨迹，并允许LLM在持续对话中渐进式更新角色认知。相较于传统方案，这种设计或许能在保持人格稳定性的同时，为长期关系建模提供更具扩展性的技术路径。需要强调的是，该架构尚未经过实践验证，文中讨论的架构只是我个人的一种设想。\n项目整体架构设计 本架构主要采取的是将作为角色属性的提示词模块化，并且通过大模型的返回动态更新模块的方案。整体上，可以将程序整体流程分为三部分：\nPrompt处理：将数据库中的各模块按照指定方法进行整合，并产生一个完整的提示词。 LLM循环处理信息:LLM接收整合后的Prompt以及各项内容，然后决定是否向本地重新发送请求进一步信息；当所需信息完整了，即输出。 模块更新机制:根据LLM的回答，重新动态的更新相应模块的参数，以维持一个稳定的状态。 可以通过以下图来理解架构的运行流程：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 #大致流程描述 用户输入：用户通过输入框或接口向系统发送请求或指令。 短期记忆：系统取出短期内的记忆提供一个指定窗口大小的上下文。 整合模块内容：系统从模块库中提取模块内容，并将其整合到请求中。 步骤二：中央LLM处理 整合模块：将短期记忆、用户输入和模块内容整合到一起，形成完整的请求内容。 中央LLM：整合后的请求内容被发送到中央LLM进行处理。 请求处理结果：中央LLM处理完成后，判断是否需要进一步请求： 进一步请求：如果需要进一步请求，则返回指定的指令，系统从记忆库和模块库中调用指定内容。 请求完成：如果请求完成，进入下一步。 步骤三：模块分割与处理 模块分割：将处理完成的请求内容分割成不同的模块，以便后续处理。 模块处理：根据分割后的模块内容，分别进入以下模块进行处理： 命令模块：处理命令相关的任务。 思考与对话模块：处理思考和对话相关的任务。 步骤四：模块更新与记忆更新 模块更新：根据处理结果，更新相关模块的内容。 记忆更新：将处理过程中产生的新记忆信息更新到记忆库中。 步骤五：对话输出 对话输出：将处理完成的对话内容输出给用户。 接下来将对架构中的各内容进行具体说明。\n模块库 模块库是一个带有许多分区的数据库。每个分区的内容是对人格模拟中所需的某个功能的集成。在使用时，我们期望这些分区可以共同作用，使大模型更明确的像角色一样进行行为。比如， 心情区模块 可以作为一个维护着情绪类型：程度的字典。在允许模型主动更新这个模块中的情绪的前提下，我们就能在某个程度上保证了大模型可以做到前后情绪一致。\n需要注意的是，以下的模块只涉及到了其功能和概念，并没有限制实现方法。大致模块如下：\n系统指令区 -不可改变的 角色基底区 -不可改变的 性格基底区 -不可改变的 角色性格区 -随时可变的 心情区 -随时可变的 角色画像区 -随时可变的 重要事件区 -随时可变的 环境区 -随时可变的 形象区 -随时可变的 扩展区 - 具体模块说明如下。\n系统指令区 系统指令区起到一个向LLM介绍具体操作的部分。其大约可以包括以下内容： 说明回答格式，按照指定格式作答，方便后续的模块分割和处理 说明指令集，让LLM确认可处理的指令，对接后面的模块更新部分以及进一步请求部分 介绍其他模块，方便模型理解其他模块的作用 角色基底区 角色基底区用于构建一个所有角色都必须遵循的底线准则。例如，你应当拥有自己的意愿、你应当珍视生命等。 这一部分不会允许进行改变。 性格基底区 性格基底区用于构建属于单独角色的最根本的性格。为了维持角色根本性格的稳定性，这一部分同样也不允许改变。 这个方式可以在允许角色自由发展个性的前提下保留“本心”，可以让角色更加可控，也不至于产生过于离谱的偏离初始人设的情况。 角色性格区 角色性格区维持着角色的“个性”。 角色性格区允许LLM根据事件和想法自由更改其中的内容。其中包含着LLM当前的性格。这在一定程度上可以模拟到现实中性格随相处改变的情况。 心情区 心情区用于维护角色当前的心情。在实际应用场景中，心情区应当是不可见的，可以支持LLM背后的情绪活动。心情区同样由LLM自身更改和维护。 心情区会显示的体现出当前角色的心情，这样就可以避免LLM的情绪变化过度离谱，也可以让情绪变化更自然。 可以让心情动态控制一些操作，比如心情不好的情况下会拒绝去进行比较多的联想。 角色画像区 角色画像区可以动态的维护LLM对于各个对象的印象。比如对于用户的印象，对于自己的印象，乃至于对于更多角色的印象。 角色画像区会由LLM在交互的过程中主动建立和更新。通过这种方法，可以实现一定程度上的“不断了解”。 该区域的存在也可以让LLM对待其他人（用户等）的过程更加自然。这也是对现实中对他人建立一个印象的模拟。 重要事件区 这一部分与ChatGPT的记忆能力类似。角色可以将一定量的简短信息记录到一个有限长度的区域中，以此来模拟他们心中最重视，不会遗忘的事情。 环境区 这一部分会维护两个内容：角色所处的环境，和角色所拥有的物品。 两个部分都是动态更新的，由LLM主动更新。这种显式说明情况的方式可以在一定程度上减少无中生有的情况。 形象区 这一部分会动态维护LLM自身的虚拟形象。 在实际的交互过程中，也许LLM可以对自己形象中的组成进行互动，并且更新自己的形象。 也是为了避免大模型的“幻觉”而采取的措施。 扩展区 考虑到模块化的便利性，也许可以在后来加入进一步的新模块，以此更好的实现角色。 模块系统的核心是由LLM来动态更新各模块，并以此实现角色的连续性。在“灵魂织者”架构中，动态更新机制的核心在于： LLM通过生成结构化指令（如JSON或特定标记语言），直接指定目标模块的更新内容与操作类型，系统通过指令解析器验证并执行修改。\n记忆库 记忆库是处理长期记忆的核心，也是让角色更加自然的关键。在本架构中，记忆库大约可以分为以下几个部分：\n事件库：进行体系化的事件归档。 日志库：记载每日的日志。 日记库：让LLM进行符合角色的主观记录。 短期上下文窗口：一定长度的完整上下文内容。包括思考记忆和对话记忆。 （除短期上下文窗口，剩余几个部分的记录不面向思考记忆） 以下对各个模块进行具体分析。\n事件库 事件库是对于人记忆中“事件”这种概念的抽象。例如，“讨论与做菜有关的话题”、“共同阅读某本书” 一个事件大约包括以下属性： 事件名称 事件开始记录时间 事件最后更新时间 事件缩略内容 事件具体内容 相关事件（一个指向其他数个事件的能力） 一个事件大约支持以下功能（通过LLM发送指令控制）： 按照日期检索事件 按照关键词检索事件名称 按照关键词检索事件内容 创建新事件 修改事件的各项属性 读取事件的各项属性 通过相关事件来跳转到其他的事件（深度可控，可以加入有情绪等因素来进一步动态调整深度） 事件的存在是为了方便模型去意识到某件事，从而给出更加完善的答复。同时，相关事件的机制模拟了人脑“联想”的过程，可以让记忆能力更加完善。 日志库 ​\t日志库是对模型日常最主要的记录部分。根据日志的功能和目的上的区别，大约可以分为以下两类日志：\n自动日志 自动记录直接对模型输出的内容进行处理并记录。自动记录包括操作记录和对话记录。操作记录记载着模型返回中使用的指令，方便追溯其行为；对话记录则是直接记录了返回中的对话内容。\n主动日志 主动日志由模型进行主动书写。主要是对今日内容的小总结。其用途主要在于当模型尝试回忆某天发生的事情时，可以快速定位到那一天情况的大致概览。\n日记库 ​\t日记库是一个对正常人“写日记”行为的模拟。与主动日志不同，日记库没有书写的硬性要求。模型会按照角色在渴望进行“写日记”的操作时进行写日记的行为。\n短期上下文窗口 ​\t短期上下文窗口是传统的LLM的上下文内容。这个部分会提供一定的具体上下文来让对话更加自然。具体内容从自动日记中调取，窗口大小可随着心情的改变而动态调整。\n用户交互\u0026amp;Prompt整合\u0026amp;模型进一步请求处理 在实际交互中，用户与应用的交互遵循以下过程：\n1 2 3 4 5 6 7 #文字说明如下： 用户通过图形化界面向程序发送请求 提示词调度器向LLM发送处理后的指定信息 LLM需要进一步请求，向程序发送指令 指令解码器解析指令，并通过提示词调度器向LLM发送进一步信息 重复进行，直到LLM信息需求足够 进入返回内容处理模块 返回内容处理模块 根据架构，模型的返回值分为两种情况：\n当需要进一步请求时：模型的返回值仅包含指令 当请求结束时：模型的返回值可以分为以下三个部分： 指令区：用于更新各模块的内容，或对输出内容进行一定的处理 思维区：思维区会保持一定长度，但不会进行长时间的保留，思维区的作用是维持思考的连续性 对话区：模型实际的回复 对于输出格式的指定，在系统指令区中指定。 总结 本架构的目的是以最高效果来模拟人格。通过模块化和动态更新方案实现人格的稳定和持续性。不过遗憾的是，本方案也许对模型的回复速度和质量要求有些过高。因此暂时难以实现。\n同时，较为高的反应时间也意味着很难接入live 2D等方案。不过也许可以考虑live2d等内容单独一个模型控制，数个模型协同等方案。\n希望在后续过程中可以加入一些有趣的尝试，比如让其接入由其他LLM来模拟的RPG环境，以观察其人格的变化等。\n当硅基载体开始尝试承载碳基生命的温度，\u0026ldquo;灵魂织者\u0026quot;的构想如同普罗米修斯的火种，在数字荒原上点亮了第一簇人格化的篝火。我们以模块为经线，记忆为纬线，在Transformer的神经网络中编织着关于\u0026quot;存在\u0026quot;的隐喻——那些被量化的情绪波动、被拓扑化的人际联结、被向量空间重新诠释的成长轨迹，都在试图回答一个古老的命题：何为意识的连续性？\n这项实验性架构的终极愿景，并非创造完美无缺的数字生命，而是在有限算力的画布上，勾勒出认知迭代的动力学图谱。正如潮汐在月球的引力中寻找规律，AI人格的塑造过程或许终将揭示：记忆的潮起潮落间，那些被保留的认知沉淀与主动遗忘的空白，共同构成了数字生命的潮间带生态。\n未来的道路仍布满迷雾，从模块共振引发的蝴蝶效应，到记忆压缩造成的认知褶皱，每个技术细节都可能成为阿莉阿德涅之线的断裂点。但正是这种在确定性架构与混沌演化之间的微妙平衡，让这场关于数字灵魂的编织实验，成为了人类叩问智能本质时最诗意的技术注脚。\n————DeepSeek\n","date":"2025-03-01T00:00:00Z","image":"https://SJTdreams.github.io/p/%E5%9F%BA%E4%BA%8Eprompt%E7%9A%84llm%E4%BA%BA%E6%A0%BC%E6%A8%A1%E6%8B%9F%E6%9E%B6%E6%9E%84-%E7%81%B5%E9%AD%82%E7%BB%87%E8%80%85/title_hu_cb99ead11f19b62b.jpg","permalink":"https://SJTdreams.github.io/p/%E5%9F%BA%E4%BA%8Eprompt%E7%9A%84llm%E4%BA%BA%E6%A0%BC%E6%A8%A1%E6%8B%9F%E6%9E%B6%E6%9E%84-%E7%81%B5%E9%AD%82%E7%BB%87%E8%80%85/","title":"基于Prompt的LLM人格模拟架构 - 灵魂织者"},{"content":" 下载链接（蓝奏云）：https://wwts.lanzoub.com/iw4H82ol03kb\nUnity UI 系统使用指南：从零到一构建高效 UI 管理模块 本篇为Unity UI 管理系统，适合中小型项目使用。无论你是个人开发者还是小团队，这套系统都能帮你快速搭建 UI 界面，同时保持代码的整洁和可维护性。\n本文将带你从 需求分析 到 实际使用，一步步掌握这套系统的核心功能和使用方法。我会尽量用通俗的语言，结合实例，让你轻松上手。\n一、需求分析：为什么需要这套系统？ 在 Unity 中，UI 管理是一个常见的痛点。随着项目规模的扩大，UI 窗口越来越多，代码变得越来越混乱。以下是我在开发中遇到的一些问题：\n窗口管理混乱：每个窗口都需要手动管理 SetActive，代码重复且难以维护。 资源占用高：所有窗口常驻内存，导致性能问题。 扩展性差：新增功能需要修改多处代码，容易引入 Bug。 调试困难：窗口间直接引用，耦合度高，难以定位问题。 为了解决这些问题，我设计了一套 基于配置驱动的 UI 管理系统，核心思想是：\n模块化设计：将 UI 功能拆分为独立组件，按需加载。 配置驱动：通过配置文件定义窗口属性，减少硬编码。 事件通信：通过事件系统解耦窗口间的依赖。 接下来，我会详细介绍这套系统的各个部分，并通过实例演示如何使用。\n二、系统组成：核心模块介绍 这套系统由以下几个核心模块组成：\nUIManager：负责窗口的打开、关闭和层级管理。 BaseWindow：所有窗口的基类，提供基础功能（如关闭按钮、拖拽）。 WindowConfigData：配置文件，定义窗口属性（如大小、背景、组件）。 EventSystem：事件系统，用于窗口间通信。 编辑器工具：快速配置和测试窗口。 三、快速上手：从零搭建一个窗口 1. 创建配置文件 右键菜单 → Create/UI/WindowConfigData，命名为 MainMenuConfig。 设置窗口属性： 1 2 3 4 size: 800x600 backgroundColor: #FFFFFFFF enableCloseButton: true enableDraggable: true 2. 创建窗口预制体 复制 BaseWindow 预制体，重命名为 MainMenuWindow。 在 BaseWindow 组件中绑定 MainMenuConfig。 3. 打开窗口 1 2 // 在游戏代码中调用 UIManager.Open(\u0026#34;MainMenu\u0026#34;); 四、进阶功能：动态添加组件 1. 定义组件枚举 1 2 3 4 5 6 public enum WindowComponent { Draggable, FadeAnimation, HighlightEffect } 2. 修改配置文件 在 WindowConfigData 中添加 requiredComponents 字段：\n1 2 3 requiredComponents: - Draggable - FadeAnimation 3. 动态加载组件 在 BaseWindow 中根据枚举值添加组件：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 private void AddComponents() { foreach (var component in _configData.requiredComponents) { switch (component) { case WindowComponent.Draggable: gameObject.AddComponent\u0026lt;Draggable\u0026gt;(); break; case WindowComponent.FadeAnimation: gameObject.AddComponent\u0026lt;FadeAnimation\u0026gt;(); break; } } } 五、事件通信：解耦窗口逻辑 1. 发送事件 1 2 // 当音量改变时 EventSystem.Publish(\u0026#34;VolumeChanged\u0026#34;, 0.8f); 2. 接收事件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public class VolumeDisplay : MonoBehaviour { private Text _text; void Start() { _text = GetComponent\u0026lt;Text\u0026gt;(); EventSystem.Subscribe(\u0026#34;VolumeChanged\u0026#34;, OnVolumeChanged); } void OnVolumeChanged(object data) { float volume = (float)data; _text.text = $\u0026#34;当前音量：{volume * 100}%\u0026#34;; } } 六、编辑器工具：快速配置和测试 1. 打开工具 点击菜单栏：Tools/UI/Window Config Converter。\n2. 保存窗口属性 将场景中的窗口拖入 目标窗口 字段。 点击 保存窗口属性到配置。 3. 应用配置 选择配置文件。 点击 应用配置到窗口。 七、常见问题解答 1. UI 组件无法交互 检查点： 确保 Canvas 的 Render Mode 为 Screen Space - Camera。 确保 EventSystem 组件存在。 确保 GraphicRaycaster 组件已启用。 2. 窗口未显示 检查点： 确保配置文件路径正确：Resources/UI/Configs/。 确保窗口预制体包含 BaseWindow 组件。 3. 事件未触发 检查点： 确保事件名称拼写正确。 确保接收端已订阅事件。 八、总结 通过这套系统，你可以：\n快速创建窗口：基于模板，减少重复工作。 灵活扩展功能：通过配置文件动态添加组件。 解耦窗口逻辑：通过事件系统实现模块间通信。 提升开发效率：通过编辑器工具快速配置和测试。 如果你有任何问题或建议，欢迎在评论区留言！希望这套系统能为你的项目带来帮助。🚀\n附：系统打包说明\n将以下文件夹打包为 .unitypackage： 1 2 3 4 Assets/ ├── Scripts/UI/ # 核心脚本 ├── Resources/UI/ # 配置文件、预制体 └── Editor/ # 编辑器工具 导入新项目后，按照本文步骤初始化即可。 Happy Coding! 🎮\n","date":"2025-02-15T00:00:00Z","image":"https://SJTdreams.github.io/p/unity-uisystem%E6%90%AD%E5%BB%BA/title_hu_6e140eeec8027a57.jpg","permalink":"https://SJTdreams.github.io/p/unity-uisystem%E6%90%AD%E5%BB%BA/","title":"Unity UISystem搭建"},{"content":" 遗传学模拟程序用户手册 [TOC]\n项目下载链接:[github] [蓝奏云]\n🌱 程序简介 这是一个用于模拟遗传规律的互动程序，可以帮助你：\n观察显性和隐性基因的遗传规律 模拟不同基因组合的繁殖结果 统计基因型和表型的分布比例 理解孟德尔遗传定律的实际应用 🧬 基本概念速查 基因相关 显性基因：用大写字母表示（如A），决定显性性状 隐性基因：用小写字母表示（如a），只有两个隐性基因组合时才表现性状 基因型：个体的基因组合（如AA/Aa/aa） 表型：实际表现出的性状（如高茎/矮茎） 组别功能 实验容器：每个组都是一个独立的实验环境 数据隔离：不同组的实验数据互不影响 模式切换：支持随机交配和人工杂交两种模式 历史记录：自动保存各代繁殖数据 💻 快速入门 启动程序 1 python main.py 出现 \u0026gt;\u0026gt;\u0026gt; 提示符表示启动成功\n基础指令速查 指令 功能 示例 /help 查看帮助文档 /help /add 添加新基因 /add A a 高茎 矮茎 /create 创建新组 /create 豌豆组 /random 随机生成个体 /random 豌豆组 10 2 /run 执行一代繁殖 /run 豌豆组 /show 查看统计数据 /show 豌豆组 -details 📚 详细使用指南 第一步：定义基因 1 /add [显性符号] [隐性符号] [显性性状] [隐性性状] 示例：\n1 2 /add A a 高茎 矮茎 /add B b 圆粒 皱粒 💡 小贴士：基因符号必须为单个字母，显性用大写\n第二步：创建实验组 1 /create [组名] 示例：\n1 2 /create 实验一组 /create 对照组 第三步：添加实验个体 方法1：手动添加\n1 /change [组名] [基因型] add [数量] 示例：\n1 /change 实验一组 AA add 5 # 添加5个AA型个体 方法2：随机生成\n1 /random [组名] [数量] [基因长度] 示例：\n1 /random 实验一组 20 4 # 生成20个双基因个体（如AaBb） 第四步：执行繁殖实验 1 2 3 /run [组名] # 观察结果但不保存 /write [组名] # 执行并保存结果 /runs [次数] # 连续繁殖多代 示例流程：\n1 2 3 4 /create 豌豆实验 /random 豌豆实验 10 2 # 生成10个单基因个体 /show 豌豆实验 # 查看初始分布 /run 豌豆实验 # 执行一代繁殖 第五步：查看实验结果 1 2 /show [组名] # 基础统计 /show [组名] -details # 详细数据 输出示例：\n1 2 3 4 5 6 7 8 9 10 11 12 === 实验组 统计 === 总个体数：50 基因型分布： Aa: 25 (50.00%) AA: 15 (30.00%) aa: 10 (20.00%) 表型分布： (高茎,): 数量：40 占比：80.00% 基因型：Aa, AA 🧪 实验模式说明 🌐 随机交配模式 自然随机配对 适合模拟自然种群 使用指令：/mode [组名] random ✂️ 人工杂交模式 可控配对组合 适合特定基因型杂交实验 使用指令：/mode [组名] cross 💡 学习案例：豌豆实验 实验目标 观察高茎（AA/Aa）与矮茎（aa）豌豆的杂交结果\n操作步骤 添加基因定义\n1 /add A a 高茎 矮茎 创建实验组\n1 /create 豌豆实验 添加亲本\n1 2 /change 豌豆实验 AA add 10 # 10株高茎纯合体 /change 豌豆实验 aa add 10 # 10株矮茎 设置杂交模式\n1 /mode 豌豆实验 cross 执行杂交\n1 /run 豌豆实验 分析结果\n1 /show 豌豆实验 -details 📥 LOAD指令详解 指令作用 /load [文件路径] 可以批量执行预先编写好的实验指令文件，适合：\n保存常用实验方案 快速重建复杂实验 课堂演示重复实验 文件格式要求 使用普通文本文件（建议.txt扩展名） 每行一条有效指令 支持#号注释 使用UTF-8编码 使用步骤 用记事本创建指令文件 每行写一个有效命令 保存时选择UTF-8编码 在程序中执行/load 文件路径 📋 实例代码库 基础示例：单基因实验 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 BASH# 保存为 basic_exp.txt # 第一步：定义基因 /add A a 高茎 矮茎 # 第二步：创建实验组 /create 单基因实验 # 第三步：生成初始种群 /random 单基因实验 20 2 # 第四步：设置杂交模式 /mode 单基因实验 cross # 第五步：执行三次繁殖 /runs 3 # 第六步：查看最终结果 /show 单基因实验 -details 进阶示例：双基因自由组合 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 BASH# 保存为 two_genes.txt # 定义两个性状基因 /add A a 高茎 矮茎 /add B b 圆粒 皱粒 # 创建双基因实验组 /create 双基因实验 # 添加特定基因型亲本（AABB × aabb） /change 双基因实验 AABB add 5 /change 双基因实验 aabb add 5 # 设置杂交模式 /mode 双基因实验 cross # 执行两代繁殖 /runs 2 # 保存最终结果 /save 双基因结果 课堂演示脚本 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 BASH# 保存为 class_demo.txt # 初始化设置 /add M m 正常 白化 /add C c 能尝味 不能尝味 # 创建对照组 /create 对照组 /random 对照组 30 4 # 创建实验组 /create 实验组 /change 实验组 MmCc add 10 /change 实验组 mmcc add 10 # 批量执行5代繁殖 /mode 实验组 cross /runs 5 # 对比两组结果 /show 对照组 /show 实验组 -details 🛠️ LOAD使用技巧 渐进式实验：分阶段保存不同脚本\n1 2 3 BASH/load 01_初始化基因.txt /load 02_创建对照组.txt /load 03_创建实验组.txt 参数化实验：修改数字快速生成不同规模实验\n1 2 BASH# 修改数量参数即可生成不同规模种群 /random 实验组 ${数量} 4 错误处理：遇到错误时会显示行号\n1 2 TEXT[行5] 执行: /add A a 高茎 !! 行5执行失败: 参数数量错误，需要4个参数 快速重建：保存成功实验配置\n1 2 3 4 BASH# 保存当前状态 /save 成功案例 # 下次使用时 /read 成功案例 💼 实战案例包 案例1：三代显性追踪 1 2 3 4 5 6 7 BASH# 保存为 three_generations.txt /add H h 正常 亨廷顿症 /create 家族追踪 /random 家族追踪 10 2 /show 家族追踪 /runs 3 /show 家族追踪 -details 案例2：多性状组合实验 1 2 3 4 5 6 7 8 9 10 BASH# 保存为 multi_traits.txt /add T t 卷舌 平舌 /add E e 双眼皮 单眼皮 /add F f 有酒窝 无酒窝 /create 三性状实验 /random 三性状实验 50 6 /mode 三性状实验 random /runs 5 /write 三性状实验 案例3：异常情况测试 1 2 3 4 5 6 BASH# 保存为 error_test.txt # 测试各种错误情况 /add X y 显性 隐性 # 应该报错（显隐基因相同） /create /read 不存在的组 /change 测试组 AAA add 10 📌 使用注意事项 文件路径建议使用英文命名 复杂实验建议每10行添加注释 遇到编码问题可使用Notepad++保存为UTF-8格式 重要实验前先用小数据量测试脚本 💡 教学建议：可让学生先手动操作理解流程，再用LOAD指令进行大规模实验对比，加深对遗传规律的理解。\n❓ 常见问题解答 Q1：为什么提示\u0026quot;基因长度不符\u0026quot;？ A：同一实验组的所有个体必须具有相同的基因组合长度，添加新个体时请保持与现有个体相同的基因位数\nQ2：如何比较不同代的实验结果？ A：可以为每代创建不同组（如F1代、F2代），使用/list指令查看各组数据\nQ3：为什么隐性性状没有出现？ A：检查：\n是否正确定义隐性性状 实验个体是否包含隐性基因 繁殖代数是否足够 Q4：如何保存实验结果？ A：程序运行期间数据保存在内存中，关闭前可以使用/save指令保存组状态\n🚀 进阶技巧 多基因实验：添加多个基因定义（如同时研究茎高和种子形状） 历史回溯：通过创建多个组保存不同阶段的实验数据 批量操作：使用/load指令从文件批量执行命令 性状组合：在详细统计模式查看多基因性状组合 📊 遗传规律验证 通过本程序可以验证：\n分离定律（子一代性状分离比） 自由组合定律（多对性状组合） 显隐性关系 基因型与表型对应关系 📌 提示：开始实验前建议先用/help查看完整指令列表，遇到问题可尝试输入/help [指令名]查看具体帮助\n","date":"2025-02-15T00:00:00Z","image":"https://SJTdreams.github.io/p/%E5%AD%9F%E5%BE%B7%E5%B0%94%E3%81%AE%E9%81%97%E4%BC%A0%E5%AD%A6%E6%A8%A1%E6%8B%9F%E7%A8%8B%E5%BA%8F/title_hu_47dfc59993ae8a18.jpg","permalink":"https://SJTdreams.github.io/p/%E5%AD%9F%E5%BE%B7%E5%B0%94%E3%81%AE%E9%81%97%E4%BC%A0%E5%AD%A6%E6%A8%A1%E6%8B%9F%E7%A8%8B%E5%BA%8F/","title":"孟德尔の遗传学模拟程序"},{"content":"Github使用攻略 在部署博客的过程中由于对github缺乏使用经验，导致了很多问题。于是决定在现在完成后记录一下github的使用。\n前排提示：如果你是一个萌新，建议直接看具体工作流示范（从0开始）部分！\n[TOC]\n2020年GitHub的日志数达到了8.6亿条，活跃代码仓库达到了5,421万个，活跃开发者数达到了1,454万人，拥有超过3,100万开发人员和9,600多万个存储库。\nGithub和Git的具体概念 首先，github和git是两个不同的概念。GitHub 本身是一个基于web的服务平台，其通过提供git仓库的托管进行服务。而git则是开源的分布式版本控制系统，两者绝对不能混为一谈。\ngit并不是github独有的。包括Bitbucket、SourceForge、Gogs、Gitbucket、GitLab、Gitee、Azure DevOps、Gitea在内的多个平台使用的都是git。\ngit的具体概念 Git 是一个开源的分布式版本控制系统，用于敏捷高效地处理任何或小或大的项目。\nGit 是 Linus Torvalds 为了帮助管理 Linux 内核开发而开发的一个开放源码的版本控制软件。\nGit 与常用的版本控制工具 CVS, Subversion 等不同，它采用了分布式版本库的方式，不必服务器端软件支持。\n对象存储：Git使用内容寻址文件系统来存储内容。每个文件和目录都以对象的形式存储，并通过SHA-1哈希值进行索引。\n分支管理：在Git中，分支是一个引用（轻量级的分支）或是一个分支对象（重量级的分支）。分支切换实际上是改变当前HEAD指针的位置。\n索引（Index）：Git的索引是一个准备区，用于暂存即将提交的文件变更。\n冲突解决：当两个分支有冲突时，Git会标记出冲突的文件，需要手动解决冲突后才能进行合并。\n标签（Tag）：用于标记特定的提交，通常用于版本发布。\n仓库（Repository）：Git用来保存项目文件和版本历史的数据库。每个项目都有一个Git仓库。\n提交（Commit）：项目文件的一个快照，包括文件的内容和元数据（如作者、日期、提交信息）。\n分支（Branch）：指向特定提交的可移动的指针，用于隔离开发流程的不同部分。\n合并（Merge）：将两个或多个不同的开发历史合并在一起。\n克隆（Clone）：创建一个仓库的副本，包括所有文件和提交历史。\n远程仓库（Remote Repository）：托管在服务器上的仓库，可以是GitHub、GitLab等。\ngithub的具体概念 GitHub是一个面向开源及私有软件项目的托管平台，因为只支持Git作为唯一的版本库格式进行托管，故名GitHub。GitHub拥有1亿以上的开发人员，400万以上组织机构和3.3亿以上资料库。\n代码托管：GitHub允许用户托管Git仓库，并提供了一个图形界面来浏览代码、提交历史、分支和标签。\n协作工具：GitHub提供了issues（问题跟踪系统）、pull requests（代码审查和合并请求）、wikis（项目文档）和项目看板等工具，以支持团队协作。\n社交功能：GitHub有关注（following）、星标（starring）、观察（watching）等社交功能，允许用户跟踪项目和开发者的活动。\n集成和自动化：GitHub提供了API和Webhooks，允许开发者集成外部服务和自动化工作流程。\n代码审查和合并：通过pull requests，GitHub支持代码审查和讨论，确保代码质量，并简化合并流程。 Github的使用 GitHub允许你创建一个远程库。需要通过git来将本地的库同步到github中。\nGithub允许你提交一个SSH密钥到账号。SSH允许你无需账号密码来同步文件。\nGithub包含一个Issues，用于追踪项目中的错误和功能请求。可以在仓库的页面上找到New issue，填写相关信息后提交。\nGithub允许你通过Pull Requests来请求将某个分支的变更合并到主分支，便于代码审查。在仓库页面，点击\u0026quot;Pull requests\u0026quot;，然后点击\u0026quot;New pull request\u0026quot;，选择要合并的分支，添加更改说明后提交。\nGithub包含一个Wikis，可以在仓库中托管项目文档。在仓库页面，点击\u0026quot;Wiki\u0026quot;标签，然后点击\u0026quot;Add or edit pages\u0026quot;，创建或编辑文档页面。\nGithub包含GitHub Actions可以实现自动化部署和持续集成（CI/CD）。例如在同步仓库时自动更新readme等操作。若要使用，则需在仓库的.github/workflows目录下创建一个YAML文件，定义工作流程和触发条件。\nGithub还包含Stars（点赞/关注）、Forks（克隆）、Watching（订阅）等内容。这是一种用户间的互动。\nGithub可以创建组织，方便同步文件。登录GitHub账户，点击右上角的\u0026quot;+\u0026ldquo;号，选择\u0026quot;New organization\u0026rdquo;，填写组织信息后创建。在组织的页面，点击\u0026quot;Teams\u0026quot;，然后点击\u0026quot;New team\u0026quot;，设置团队名称和成员。\nGit的使用 git的本地仓库包含三个部分：其一是工作目录，其保存着实际的文件。其二是暂存区，类似于缓存，保存临时改动。其三是HEAD区，指向最后一次提交的结果。\ngit init：初始化一个git仓库。\ngit clone path：克隆一个本地仓库。把path换成具体路径。\ngit clone [url]：克隆一个远程仓库。包含https克隆和SSH克隆。https的链接通常类似于这样：https://github.com/username/repository.git。SSH的链接通常类似于这样：git clone git@github.com:username/repository.git。\ngit add \u0026lt;filename\u0026gt;：添加文件到暂存区。如果filename为**.**（就是一个点）就是指当前目录下的所有文件。文件名不添加路径则是当前目录下的文件。当选择的是文件夹会递归的添加其下的所有文件。\ngit add -u：这个命令只添加已经跟踪的文件（即之前已经添加到Git仓库的文件），不包括新文件。 git add -A / git add --all：这些命令添加所有变化的文件和新文件。 git commit -m \u0026quot;代码提交信息\u0026quot;：将改动提交到HEAD。\ngit push origin master：将这些改动推送到远端仓库。其中，origin是远程仓库的默认名称，当你克隆一个远程仓库时，Git 自动将远程仓库的引用设置为 origin。这个名称是可替的。master是提交分支名，可以自行更改。\ngit remote -v：查看远程仓库的URL，origin 会显示在列表中。\ngit remote add new_origin \u0026lt;repository_url\u0026gt;：添加一个新的远程仓库，命名为new_origin。\nmaster是git的默认分支。\ngit checkout feature_x：切换到某个分支。feature_x是该分支的名称。\ngit checkout -b feature_x：创建并切换到某个分支，feature_x是该分支的名称。\ngit branch -d feature_x：删除某个分支，feature_x是该分支的名称。\ngit push origin \u0026lt;branch\u0026gt;：推送这个分支。没有推送的分支在远程上是不可见的。\ngit pull [remote] [branch]:从远程仓库拉取代码变更，并尝试将这些变更自动合并到当前本地分支。[remote]：这是远程仓库的名称，默认是 origin。branch：这是远程仓库中你想要拉取的分支名称。如果你不指定 [remote] 和 branch，Git 会默认拉取 origin 远程仓库中与当前本地分支关联的分支的变更。\ngit merge \u0026lt;branch\u0026gt;：合并一个分支到当前分支。branch是该分支的名称（\u0026lt;\u0026gt;是不要的）。\ngit diff \u0026lt;source_branch\u0026gt; \u0026lt;target_branch\u0026gt;：预览两个分支的差异。\ngit log：获得提交ID。\ngit tag 1.0.0 id：创建一个叫做 1.0.0 的标签。id指提交 ID 的前 10 位字符。\ngit checkout -- \u0026lt;filename\u0026gt;：使用 HEAD 中的最新内容替换掉你的工作目录中的文件。\ngit fetch [remote]：从远程仓库获取数据，并下载远程分支的更新和提交，但不会自动合并这些更改到你的本地分支。\ngit reset [--hard] [\u0026lt;commit\u0026gt;]：重置当前HEAD和索引（暂存区）。[--hard]：这是一个可选的选项，表示重置时连同工作目录一起重置，即放弃所有本地未提交的更改。[\u0026lt;commit\u0026gt;]：这是一个占位符，表示你想要重置到的特定的提交（commit）。可以是一个分支名、标签或者提交的哈希值。\ngitk：图形化git。\ngit config color.ui true/false开启/关闭彩色输出。\ngit config format.pretty oneline：显示历史记录时，每个提交的信息只显示一行。\ngit add -i：交互式添加文件到暂存区。\n具体语法实例： 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 # 初始化一个Git仓库 git init # 克隆一个远程仓库到本地 git clone https://github.com/username/repository.git # 添加文件到暂存区 git add index.md # 添加所有变化的文件和新文件 git add -A # 添加已经跟踪的文件（不包括新文件） git add -u # 提交暂存区的更改到本地仓库 git commit -m \u0026#34;Add index.md with Github usage log\u0026#34; # 查看远程仓库的URL git remote -v # 添加一个新的远程仓库引用 git remote add origin https://github.com/username/repository.git # 推送本地仓库的更改到远程仓库 git push -u origin master # 从远程仓库拉取代码变更，并合并到当前本地分支 git pull origin master # 合并远程分支的更改到当前分支 git merge origin/master # 显示两个分支的差异 git diff master feature_x # 查看提交历史 git log # 创建一个标签 git tag 1.0.0 \u0026lt;commit_id\u0026gt; # 检出标签对应的提交 git checkout 1.0.0 # 检出HEAD中的最新内容替换工作目录中的文件 git checkout -- index.md # 从远程仓库获取数据，但不自动合并 git fetch origin # 重置当前HEAD和索引（暂存区）到指定的提交 git reset --hard \u0026lt;commit_id\u0026gt; # 重置当前HEAD和索引（暂存区）到远程分支的状态 git reset --hard origin/master # 打开图形化Git工具 gitk # 开启/关闭彩色输出 git config --global color.ui true # 显示历史记录时，每个提交的信息只显示一行 git config --global format.pretty oneline # 交互式添加文件到暂存区 git add -i 身份认证 身份认证主要涉及与远程仓库的交互，例如推送（push）和拉取（pull）代码。在进行这些操作时，会进行身份认证。 在“Settings”页面，选择“Developer settings”。 点击“Personal access tokens”，然后点击“Generate new token”。 选择需要的权限范围，生成个人访问令牌。 复制生成的 PAT 并妥善保管，因为之后无法再次查看完整的 PAT。 生成 PAT 是为了在使用 HTTPS 认证时，可以使用 PAT 代替密码进行身份验证，提高安全性，避免在代码操作过程中频繁输入密码，同时可以为不同的用途生成不同的令牌，便于管理和权限控制。 配置 Git 用户信息 在本地计算机上打开终端或命令行工具. 使用命令 git config --global user.name \u0026quot;Your Name\u0026quot; 设置全局用户名. 使用命令 git config --global user.email \u0026quot;your_email@example.com\u0026quot; 设置全局用户邮箱. 生成 SSH 密钥对（使用 SSH 认证） 生成SSH可以避免每一次登录都要输入账号密码 打开终端，输入命令 ssh-keygen -t rsa -b 4096 -C \u0026quot;your_email@example.com\u0026quot; 生成 SSH 密钥对. 按照提示操作，输入文件保存路径和密码（可选）. 生成的公钥文件通常位于 ~/.ssh/id_rsa.pub，私钥文件位于 ~/.ssh/id_rsa. 添加 SSH 公钥到 GitHub 如果不添加，GitHub 无法识别本地计算机的身份，使用 SSH 认证时会出现权限拒绝的错误。 登录 GitHub 账户，点击右上角的头像，选择“Settings”. 在左侧菜单选择“SSH and GPG keys”，点击“New SSH key”. 输入 SSH 密钥的标题，将公钥内容粘贴到“Key”框中，点击“Add SSH key”. 克隆远程仓库 使用 SSH URL 克隆远程仓库，例如 git clone git@github.com:username/repository.git. 在首次克隆时，可能会提示输入 SSH 密钥的密码（如果设置了密码）. 推送和拉取代码（使用 SSH 认证） 在本地仓库中进行代码更改后，使用 git push 推送代码到远程仓库. 使用 git pull 从远程仓库拉取代码更新. 由于使用了 SSH 密钥认证，不需要输入用户名和密码. 使用 HTTPS 认证（不推荐） 每次操作都需要输入用户名和密码，增加了操作的复杂性，且在安全性上可能不如 SSH 认证。 使用 HTTPS URL 克隆远程仓库，例如 git clone https://github.com/username/repository.git. 在推送和拉取代码时，输入用户名和密码（或 PAT）进行身份验证. 如果使用 PAT，可以在 Git 命令中输入 username 和 PAT 作为凭证. 具体工作流示范（从0开始） 一、注册及相关准备工作 在具体使用Github前，需要先注册一个账号。在国内由于“多方面”原因可能比较难以登上。如果遇到了无法登上的问题，可以考虑更换浏览器或者使用Watt Toolkit 访问GitHub官网，点击右上角的Sign up按钮，按照提示填写信息（邮箱，密码，用户名）创建一个新的GitHub账户。 登录到GitHub账户后，点击右上角的**+按钮，选择New repository**。 在创建仓库界面，要注意的选项有以下几项，根据自己的需求来决定： Repository name（必填）：这个项会决定仓库的名称。 Public/Private（默认public）：选择可以决定仓库是否公开，也就是别人能不能看到。 Add a README file（可选）：可以在仓库中加入一个介绍的文本，方便写项目介绍、更新日志之类的东西。 Add .gitignore（可选）：创建一个上传选择文件，可以在仓库上传时选择性的忽略一部分文件。 Choose a license（可选）：可以决定别人要怎么对待你这个项目。简单来说可以从以下几个选择一个，具体的可以查Github许可证使用手册中文版： MIT许可证：最宽松，只要保留版权和许可声明，就可以随意使用、修改和分发代码，适合希望代码被广泛传播的个人或小项目。 Apache许可证：也很宽松，和MIT类似，但多了专利保护条款，适合大型项目，尤其是涉及多个开发者和组织合作的项目。 GPL许可证：要求修改后的代码也必须开源，适合希望保持项目开源精神，防止代码被闭源的项目。 BSD许可证：比较宽松，允许自由使用和修改代码，但需保留版权声明，适合希望代码能被广泛应用，包括商业用途的项目。 二、本地git下载和配置 通过链接简单安装git到电脑上：git windows版下载地址\n配置你的用户名和邮箱，这个会决定你提交更改时显示的用户信息。输入win + R,输入cmd并打开。\n在其中输入以下代码。注意将Your Name换成你自己的用户名，your_email@example.com换成你自己的邮箱地址：\n1 2 git config --global user.name \u0026#34;Your Name\u0026#34; git config --global user.email \u0026#34;your_email@example.com\u0026#34; 三、身份认证 上传代码到仓库时需要身份认证。如果每一次都输入账号密码就很麻烦，所以采用SSH密钥认证来跳过这个步骤。\n在cmd中输入以下片段。将your_email@example.com替换成你在GitHub上注册的邮箱地址：\n1 ssh-keygen -t rsa -b 4096 -C “your_email@example.com” 登录GitHub，点击右上角头像，选择Settings。\n在左侧菜单中选择SSH and GPG keys，点击New SSH key。\n在Title字段中，为SSH密钥添加名字；在Key字段中，将id_rsa.pub文件的内容复制粘贴进去，然后点击Add SSH key确认添加。\n继续在cmd中输入，将Your Name改为你的用户名，将your_email@example.com改为你的邮箱：\n1 2 git config --global user.name \u0026#34;Your Name\u0026#34; git config --global user.email \u0026#34;your_email@example.com\u0026#34; 四、下载仓库和拉取分支（更改）到本地 打开你要找的仓库，找到界面中的***\u0026lt; \u0026gt; Code***选项，打开复制链接。\n打开cmd，输入以下代码，这会将项目下载到指定目录下：\n1 2 cd \u0026lt;你要的具体路径\u0026gt; git clone \u0026lt;刚才复制的链接\u0026gt; 拉取分支通过以下操作。如果你不知道分支是什么，可以理解为延申出去的不同方面的更改：\n1 2 3 4 5 6 7 8 #打开到指定目录 cd \u0026lt;仓库路径\u0026gt; #切换到你要的指定分支 git checkout main #拉取指定分支 git pull origin \u0026lt;分支名称\u0026gt; #拉取当前分支的最新内容 git pull 五、 上传文件到仓库 首次上传时，打开cmd，执行以下操作：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 cd \u0026lt;你要上传的文件路径\u0026gt; #初始化git，这会创建一个.git隐藏目录，用于存储Git的元数据和对象数据库 git init #创建一个分支，用于提交，并切换到这个分支上 git checkout -b \u0026lt;分支名称\u0026gt; #选择你要上传到的仓库 git remote add origin \u0026lt;仓库链接\u0026gt; #决定要上传的文件 git add \u0026lt;fileName\u0026gt; #如果你希望上传当前目录所有文件，用这个 git add . #先提交到本地仓库上 git commit -m \u0026#34;提交信息\u0026#34; #发送分支到目标仓库 git push -u origin new-branch-name 在要更新时，可以简单的这么做：\n1 2 3 git add . git commit -m \u0026#34;Update\u0026#34; git push 总结 Git 作为一个开源的分布式版本控制系统，以其高效和灵活性被广泛应用于各种项目中。而 GitHub，作为一个基于 Web 的服务平台，提供了 Git 仓库托管和丰富的协作工具，极大地方便了开发者之间的代码共享和项目管理。 本篇涉及了了 Git 的核心特性，和基础语法，大概阐释了涉及到的概念。可以作为我自身的查档用，也有一定的参考价值。 GitHub 的主要语法和操作实际上基于 Git，而 GitHub 则作为远程仓库的角色，使得代码的远程托管和管理变得更加便捷。希望本文能帮助您更好地理解和使用 Git 和 GitHub，提高您的工作效率，并在开源社区中发挥更大的作用。 相关资料 * git - 简明指南 * Git 教程|菜鸟编程 * Github-百度百科 * Git-百度百科 * Git官方文档 * Git维基百科 * Github维基百科\n","date":"2025-01-02T00:00:00Z","image":"https://SJTdreams.github.io/p/github%E4%BD%BF%E7%94%A8%E6%97%A5%E5%BF%97/title_hu_e64f875ba0e4c347.png","permalink":"https://SJTdreams.github.io/p/github%E4%BD%BF%E7%94%A8%E6%97%A5%E5%BF%97/","title":"Github使用日志"},{"content":"通过Process在Unity中调用Python(未完成) [TOC]\n由于个人对Unity比较熟悉，因此打算将api接入unity实现对话的图形化\u0026hellip;不过大部分的模型api都是通过python来调用，因此决定通过unity来实现对python脚本的调用。经过了解，有许多文案写的是通过Python for Unity来实现调用。然而该组件实现的脚本只能放在Editor目录下，也没办法打包，只能说完全不适配。尽管还存在**UnityWebRequest等实现方式，不过决定通过Process**来实现。\n一、Process类简介 Process组件提供对计算机上正在运行的进程的访问权限。 用最简单的术语说，进程是一个正在运行的应用。 线程是操作系统分配处理器时间的基本单元。 线程可以执行进程代码的任何部分，包括当前由另一个线程执行的部分。\n​\t——————————文档介绍\n​\tProcess 类是 C# 中 System.Diagnostics 命名空间下的一个类，可以管理和控制操作系统进程。其可以调用的范围包括：\n.exe\n.bat / .cmd\n任何包含可打开该后缀的文件，比如当电脑安装了python便可以打开.py，当电脑安装了音乐播放器就可以打开.mp3。\n处理正在运行的进程，可以进行包括但不限于\n获取进程信息 终止进程 重定向输入流 重定向输出流 重定向错误流 因此，通过打开.py文件作为进程，重定向输入、输出和错误，就可以实现在Unity中调用python。\n二、Process语法 就要用得上的部分分析，包括以下几个类：\nProcess ProcessStartInfo Trace Stopwatch Debug 其中，**Process和ProcessStartInfo**是刚需，剩下的作为性能优化考虑。\nProcess ","date":"2025-01-02T00:00:00Z","image":"https://SJTdreams.github.io/p/%E9%80%9A%E8%BF%87process%E5%9C%A8unity%E4%B8%AD%E8%B0%83%E7%94%A8python/title_hu_d85d250088fb6b22.jpg","permalink":"https://SJTdreams.github.io/p/%E9%80%9A%E8%BF%87process%E5%9C%A8unity%E4%B8%AD%E8%B0%83%E7%94%A8python/","title":"通过Process在Unity中调用**Python**"},{"content":"机器学习 线性代数学习笔记 本篇是在学习过程中写的，可能会存在疏漏，欢迎补充！\n“人工智能是我们人类正在从事的最为深刻的研究方向之一，甚至要比火与电还更加深刻。”\n​\t——桑德尔·皮猜（Sundar Pichai）, 2020\n[TOC]\n本人观看地址：https://www.bilibili.com/video/BV1Pg4y1X7Pa/?spm_id_from=333.337.search-card.all.click\n1. 数学基础在AI学习中的作用 1.1 数学基础在AI研究中的必要性 理论支撑：存在大量的AI模型理论都依靠大量的数学基础。深度学习的原理上涉及到了大量的线性代数运算，如点积、矩阵乘法等。而微积分也在各种损失函数、计算梯度上运用广泛。例如训练神经网络的核心算法：反向传播，就涉及到了大量的微积分知识。大名鼎鼎的框架TensorFlow，直译就是“张量流动”。\n模型优化：在AI模型的训练过程中，优化算法如梯度下降法需要用到微积分的知识。数学能够帮助我们分析和选择最佳的学习率，从而加速模型的收敛，提高模型性能。\n数据分析：AI领域中的数据预处理、特征提取等步骤都涉及到统计学和概率论。这些数学工具帮助我们从数据中提取有价值的信息，为模型训练提供支持。例如，可用通过统计学识别异常值，处理缺失值等。而将数据标准化和归一化也涉及到数学。\n算法创新：数学是推动AI算法创新的关键。许多新的AI算法，如卷积神经网络（CNN）和循环神经网络（RNN），都是基于数学理论的创新。CNN运用了数学上的互相关运算，而RNN的时间序列也是矩阵表示的，RNN的预测还涉及到了马尔科夫链(概率论)。\n1.2 深度学习可能涉及到的数学内容 线性代数：在线性代数中，矩阵和向量运算是构建和理解神经网络的基础。矩阵的乘法、转置、逆等操作在神经网络的前向传播和反向传播中扮演着核心角色。线性代数极大程度的简化了模型的表示。\n概率论与数理统计：评估、选择模型，数据处理，决策优化等。\n微积分：涉及到梯度下降，反向传播与正向传播，处理损失函数，进行正则化，卷积操作，一些其他的优化算法（如牛顿法、拟牛顿法等）等。\n信息论：评估特征信息量，量化模型复杂度，数据压缩等。其中的交叉熵损失函数广泛运用于分类、目标检测和NLP（自然语言处理）。\n与均方误差（MSE）相比，交叉熵损失函数在处理分类问题时通常更具优势，因为它直接衡量模型对于类别分布的拟合程度，而MSE则通过最小化预测值与真实值之间的平方差来评估模型性能，这在分类问题中可能不够直观\n2. 线性代数的核心概念 2.1 矩阵的定义与应用 矩阵是线性代数中的一个基本概念，由数排成的矩形阵列，常用于线性方程组的系数表示、线性变换等。矩阵可以在某种程度上视为一个用于存放方程系数的二维系统，并且可以通过保持奇异性的行操作化简成行阶梯形。矩阵的广泛应用是线性代数中的核心，以下是矩阵的一些关键应用：\n线性方程组：矩阵可以用来表示线性方程组，通过矩阵运算可以求解方程组的解（例如可以通过高斯消元法求解），这对于理解和设计AI算法中的优化问题至关重要。\n数据表示：在机器学习中，数据通常以矩阵的形式表示，其中每一行代表一个样本，每一列代表一个特征。这种表示方法便于算法处理和分析。\n变换：矩阵可以表示线性变换，如图像处理中的旋转、缩放等操作，这些都是计算机视觉中的基础操作。\n神经网络：在深度学习中，神经网络的权重和输入数据都以矩阵的形式存在，矩阵乘法是前向传播和反向传播中的基本操作。\n2.2 单位矩阵与逆矩阵 单位矩阵和逆矩阵是矩阵理论中的重要概念，它们在解决线性方程组和线性变换中扮演着关键角色。\n单位矩阵：单位矩阵是一个方阵，其主对角线上的元素都是1，其余元素都是0。单位矩阵与任何向量执行点积，其结果等于该向量。对单位矩阵做线性变换得到的基向量不变，这是因为单位矩阵代表了线性变换中的“无操作”。\n逆矩阵：逆矩阵与原矩阵的乘积等于单位矩阵。逆矩阵可以通过解方程计算得到。非奇异的矩阵总是有逆的（可逆矩阵），而奇异的矩阵总是无逆的。奇异的方程的行列式必然为0，这就像是数字0没有逆元一样。互为逆矩阵的矩阵的行列式互为倒数。刚好1/0是未定义的，奇异的矩阵无逆矩阵。\n$$ det({A}^{-1})=\\frac {1} {det(A)} $$ 2.3 向量的基本性质 向量是线性代数中的另一个核心概念，核心要素包括方向和大小，可以视为指向某个坐标的箭头。\n范数：向量的范数是衡量向量“长度”的一种方式。默认的L2范数是向量内所有数平方和的平方根，而曼哈顿距离（L1范数）是向量内所有数的绝对值之和。这些范数在不同的应用场景中有着不同的用途，如在优化问题中，选择合适的范数可以帮助我们得到不同的优化结果。\n正交性：当两个向量是正交的时，这两个向量的点积为0。这个性质在机器学习中的特征选择和降维中非常重要，因为它可以帮助我们识别和消除特征之间的相关性。\n投影：两个向量成角度的向量的点积，等同于其中一个向量对另一个向量做投影得到的向量与另一个向量的点积。这说明可以用投影的正负确定点积的正负，一定程度上可以理解为，点积值为正的两个向量夹角必然小于90度。\n3. 线性代数的深入理解 3.1 奇异性与非奇异性 奇异性定义：简单而言，一个不具备冗余和矛盾信息的句子系统或方程组是非奇异的。非奇异方程组在一般情况下通常可解，而奇异方程在一般情况下通常不可解。这可以通过行列式法或秩判定法来判断。行列式法涉及将矩阵按照两个方向划分为数条对角线，若各自乘积之和相等则是奇异的。秩判定法则是看方阵的秩是否小于其阶数，若是，则矩阵是奇异的。\n数据科学中的应用：在数据科学中，奇异性与非奇异性的概念对于理解数据集的线性独立性非常重要。一个非奇异的数据矩阵意味着数据集中没有冗余的特征，这有助于避免在机器学习模型中出现过拟合现象。\n数值计算中的影响：在数值计算中，奇异矩阵可能导致算法的不稳定和数值误差的放大。例如，在求解线性方程组或者进行矩阵求逆时，奇异矩阵可能会导致算法失败或者结果不准确。\n3.2 秩的概念及其重要性 秩是矩阵的一个基本属性，它描述了矩阵中线性无关的行或列的最大数量，反映了矩阵所包含的“有效”信息的多少。\n秩的定义：矩阵的秩是矩阵中线性无关的行（或列）向量的最大个数。它决定了线性方程组中独立方程的个数，进而影响方程组解的情况。秩的概念在信息论中也非常重要，因为它可以衡量矩阵信息量。\n秩与数据压缩：在数据压缩和降维领域，秩的概念被用来识别最重要的特征。通过降低数据矩阵的秩，我们可以去除不重要的噪声和冗余信息，从而实现数据的有效压缩。\n秩与机器学习：在机器学习中，秩的概念可以帮助我们理解模型的复杂度。例如，在主成分分析（PCA）中，我们通过选择前几个主成分来降低数据的维度，这些主成分的数目通常与数据矩阵的秩有关。\n秩与线性方程组：在线性方程组中，系数矩阵的秩决定了方程组解的性质。如果系数矩阵的秩等于增广矩阵的秩且小于变量的数目，则方程组有无穷多解；如果秩等于变量的数目，则方程组有唯一解；如果秩小于变量的数目，则方程组无解。\n4. 行阶梯形式与高斯消元法 4.1 行阶梯形式的特点 行阶梯形式是线性代数中对矩阵进行化简的一种重要形式，它通过保持奇异性的行操作对矩阵进行化简。以下是行阶梯形式的一些关键特点：\n主元位置：每一行最左边的非0数被称为主元，主元数等于秩的值。每一行的主元必然位于上一行的右方，这保证了矩阵的上三角结构。\n全零行：矩阵的全零行只能出现在矩阵的下部，且如果出现了全零行，该矩阵是奇异的。这一点是判断矩阵是否奇异的重要依据。\n化简过程：通过将每个主元所在列的其他数字化为0得到的矩阵被称为简化行梯形式。这个过程有助于进一步简化矩阵，使其更易于处理。\n非奇异性判定：当且仅当主元的数等于阶数的矩阵是非奇异的。这意味着，如果一个矩阵在其行阶梯形式中每一行和每一列都有一个主元，则该矩阵是非奇异的，反之则为奇异。\n对角线特性：在行阶梯形式中，主对角线以下的所有元素都是0，这使得矩阵的结构更加清晰，便于进行后续的计算和分析。\n4.2 高斯消元法的应用 高斯消元法是一种用于求解线性方程组的算法，它通过行操作将增广矩阵转换为行阶梯形式或简化行阶梯形式，从而得出方程组的解。以下是高斯消元法的一些关键应用：\n方程求解：高斯消元法可以将线性方程组的系数矩阵和常数项矩阵合并为一个增广矩阵，然后通过行操作将其转换为行阶梯形式，从而求解方程组。\n算法效率：高斯消元法在数值计算中非常高效，尤其是对于大规模的线性方程组。它的效率在于能够逐步消去变量，减少计算量。\n数值稳定性：高斯消元法在执行过程中可以通过部分选主元等策略来提高数值稳定性，减少计算过程中的舍入误差。\n矩阵求逆：高斯消元法也可以用来求解矩阵的逆。通过将单位矩阵与原矩阵增广，然后执行高斯消元，可以得到原矩阵的逆矩阵。\n线性代数的基础：高斯消元法是理解线性代数中许多其他概念和算法的基础，如矩阵分解、LU分解等。\n在AI中的应用：在AI领域，高斯消元法可以用于求解优化问题中的线性方程组，如在支持向量机（SVM）的训练过程中求解拉格朗日乘子。\n通过对行阶梯形式和高斯消元法的深入理解，我们可以更好地掌握线性代数在解决实际问题中的应用，尤其是在AI领域的算法实现和数据分析中。\n5. 线性代数中的运算 5.1 向量运算 向量运算是线性代数中的基础，它们在AI领域的数据处理和特征工程中扮演着重要角色。以下是向量运算的一些关键点：\n向量-向量加法：向量加法可以通过平行四边形法则来理解，其结果向量的坐标是两个向量对应坐标的和。在实际计算中，向量加法是按元素相加的。\n向量-向量减法：向量减法可以视为求两个向量差的运算，结果向量的坐标是两个向量对应坐标的差。在实际计算中，向量减法是按元素相减的。\n向量-标量乘法：向量与标量的乘法是将向量的每个元素乘以该标量，结果向量的每个元素都是原向量对应元素与标量的乘积。\n向量-向量乘法（点积）：两个向量的点积是对应元素乘积的和。点积的结果是一个标量，它具有几何意义，如计算两个向量之间的夹角和相似度。在AI中，点积常用于特征归一化和相似性度量。\n转置：向量的转置是将行向量转换为列向量，或将列向量转换为行向量。在矩阵运算中，转置操作常用于改变数据的布局以适应特定的计算需求。\n5.2 矩阵运算 矩阵运算是线性代数中的核心，它们在AI算法的实现中至关重要。以下是矩阵运算的一些关键点：\n矩阵-向量乘法：矩阵与向量的乘法是将矩阵的每一行与向量进行点积操作，结果是一个向量。在神经网络中，这种运算用于实现前向传播和反向传播。\n转置：矩阵的转置是将矩阵的行和列互换，即原矩阵的第i行第j列元素变成转置矩阵的第j行第i列元素。转置操作在AI中用于调整数据维度，如在图像处理和特征变换中。\n矩阵-矩阵乘法：矩阵乘法是通过第一个矩阵的每一行与第二个矩阵的每一列的点积来计算的。矩阵乘法在AI中用于实现复杂的变换，如卷积神经网络中的卷积操作。\n矩阵-矩阵乘法有一个特性：两个矩阵相乘再计算行列式的值会等于这两个矩阵的行列式的积。\n$$ det(AB)=det(A)⋅det(B) $$ 而因为奇异矩阵的行列式为0，因此任何矩阵与奇异矩阵相乘都会等于0。这一点也可以说明为什么奇异的矩阵没有逆元，因为单位矩阵是非奇异的。\n线性变换：矩阵可以表示线性变换，这种变换将平面上的点映射到另一个点。在AI中，线性变换用于特征提取和数据降维，如主成分分析（PCA）。\n通过对线性代数中的向量和矩阵运算的深入理解，我们可以更好地掌握这些运算在AI领域的应用，从而在算法设计和数据分析中做出更合理的决策。\n6. 线性变换及其应用 6.1 线性变换的定义 线性变换是线性代数中的一个重要概念，它描述了一种特殊的函数，这种函数将向量空间中的元素映射到同一空间或另一个向量空间中的元素，同时保持向量加法和标量乘法的操作不变。具体来说，如果有一个函数 \\( T \\) 从向量空间 \\( V \\) 映射到向量空间 \\( W \\)，对于任意向量 \\( \\mathbf{u}, \\mathbf{v} \\in V \\) 和任意标量 \\( c \\)，满足以下两个条件，则 \\( T \\) 是一个线性变换：\n$$ T(\\mathbf{u} + \\mathbf{v}) = T(\\mathbf{u}) + T(\\mathbf{v}) $$ $$ T(c\\mathbf{u}) = cT(\\mathbf{u}) $$ 线性变换在AI领域中的应用非常广泛，包括图像处理、语音识别、自然语言处理等。例如，在图像处理中，线性变换可以用于图像的旋转、缩放和剪切等操作；在自然语言处理中，线性变换可以用于词向量的转换和文本的特征提取。\n6.2 线性变换与基向量 基向量是定义向量空间的一个关键概念，它们是一组线性无关的向量，可以用来表示空间中的任何向量。在线性变换中，基向量扮演着至关重要的角色，因为线性变换可以看作是基向量在变换下的映射。\n基向量的变换：对于一个给定的线性变换 \\( T \\)，如果我们知道基向量在 \\( T \\) 下的像，那么我们就可以确定 \\( T \\) 对空间中任何向量的作用。这是因为空间中的任何向量都可以表示为基向量的线性组合，而 \\( T \\) 的线性保证了它对线性组合的作用可以通过对基向量的作用来确定。\n矩阵表示：在实际应用中，线性变换通常通过矩阵来表示。如果 $$ \\{\\mathbf{v}_1, \\mathbf{v}_2, ..., \\mathbf{v}_n\\} $$ 是向量空间 \\( V \\) 的一组基， $$ \\{\\mathbf{w}_1, \\mathbf{w}_2, ..., \\mathbf{w}_m\\} $$ 是向量空间 \\( W \\) 的一组基，那么线性变换 \\( T \\) 可以由一个 $$ m \\times n $$ 矩阵 \\( A \\) 来表示，其中矩阵的列是基向量 $$ \\mathbf{v}_i $$ 在 \\( T \\) 下的像，以 \\( W \\) 的基向量表示。\n维度与秩：线性变换的秩，即变换后图像的维度，等于变换矩阵的秩。如果变换是非奇异的，那么秩等于基向量的数量，这意味着基向量在变换后仍然覆盖整个空间。如果变换是奇异的，那么秩小于基向量的数量，这意味着基向量在变换后覆盖的空间降维了。\n行列式与体积变化：对于非奇异的线性变换，行列式的绝对值表示变换后基向量形成的平行六面体的体积与变换前基向量形成的单位立方体体积的比率。如果行列式的值为零，则变换是奇异的，基向量在变换后形成的体积为零，即所有的基向量映射到了一个低维空间。\n面积特征：线性变换对于单位基向量的变换后的图像的面积等于行列式的值的绝对值。\n当行列式为负时，线条会颠倒。某种程度上可以把这视为负面积值。\n$$ S = |\\det(A)| $$ 6.3 离散动力系统 离散动力系统描述了系统状态在离散时间点上的变化。离散动力系统的状态更新是在一系列特定的时间间隔内进行的，这些时间间隔通常称为时间步长。其函数可表示为：\n$$ x_{t+1} = f(x_t) $$ 特征值和特征向量在离散动力系统和连续动力系统中都有应用，它们是数值代数的核心内容。对于一个马尔可夫矩阵。可以通过当前的状态向量与概率矩阵点积得到目标概率。当反复执行过程直到趋向于稳定，就表示得到的是特征向量且特征值为1。\n一个所有列向量的元素和的值为1且所有元素非负的方阵叫做马尔可夫矩阵。\n通过对线性变换和基向量的深入理解，我们可以更好地把握线性代数在AI领域的应用，从而在算法设计和数据分析中做出更合理的决策。线性变换提供了一种强大的工具，用于分析和处理高维数据，而基向量则为我们提供了一种直观的方式来理解和操作这些变换。\n7.张成和基 7.1 张成的概念与基的概念 ​\t张成不仅帮助我们理解向量之间的线性关系，还为定义和研究线性空间的结构提供了基础。\n张成的概念：张成是一定的向量能够通过一定数量的重复叠加所能达到的所有位置。具体来说，给定一个向量集合 S={v1,v2,…,vn}，这个集合的张成（span）就是所有可以表示为这些向量的线性组合的向量的集合。\n基的概念：基是一个最小（向量数量）的张成集。只有线性独立的向量才能构成基。基的向量数等同于该空间数的维度。\n7.2 基的特点 线性无关性：只有线性独立的向量才能构成基。线性独立指你无法通过同组的其他向量构成该向量。 关于线性独立与奇异性的联系：考虑一个 n*×n 的方阵 A，其列向量为 {v1,v2,…,vn}。 如果 A 的列向量是线性独立的，那么 A 是非奇异的，即 A 有逆矩阵。 如果 A 的列向量是线性相关的，那么 A 是奇异的，即 A 没有逆矩阵。 张成性：基中的向量可以线性组合生成整个向量空间中的任何向量。 最小性：基是包含在向量空间中的最小向量集合，它既线性无关又张成整个空间。 唯一性：对于给定的向量空间，基不是唯一的，但任何两个基都包含相同数量的向量，这个数量就是空间的维度。 7.3 特征基 通过特征向量构成的基被称为特征基。对于一个用于线性变换的矩阵，如果存在某一个（组）向量，通过该矩阵线性变换后只是缩放或反转，而非拉伸和扭曲，则称该基为这个矩阵的特征向量。可以用公式这样表示，其中A是一个矩阵，v是一个向量，λ是一个标量：\n$$ Av = \\lambda v $$ 在以上的定义公式中，λ 被称为特征向量 v的特征值。\n可以通过计算标量-向量乘法来代替矩阵-向量乘法，这有效的降低了计算量。\n特征向量具体的计算过程及原理： 因为特征向量在矩阵上只是缩放，因此变换后与变换前是线性相关的。设存在一个特殊的矩阵，其满足将单位向量放大m倍：\n$$ m = \\begin{pmatrix} m \u0026 0 \u0026 0 \\\\ 0 \u0026 m \u0026 0 \\\\ 0 \u0026 0 \u0026 m \\end{pmatrix} $$ 然后，我们假设这个m在某一条轴上与我们的目标矩阵缩放尺度相同。因为其处处相等，我们可以知道，其差是一个奇异的矩阵。那么便有：\n$$ det(A−λI)=0 $$ 这个方程被称为特征方程。解特征方程 ，得到特征值 λ1,λ2,…,λn。\n对于每个特征值 λi，我们需要找到对应的特征向量 vi。特征向量是一个非零的向量，且满足：\n$$ (A−λi*I)vi=0 $$ 对于每一个特征值vi，我们执行这个解方程操作。这个方程组可能有多个解，但任何非零解都可以作为特征向量。\n将特征向量归一化，方便使用。当解出来的特征值存在相同项时，不一定有特征基。\n8.PCA（主成分分析） ​\tPCA（主成分分析，Principal Component Analysis）是一种统计方法，它通过正交变换将一组可能相关的变量转换为一组线性无关的变量集，称为主成分。PCA 通常用于降维，数据压缩，特征提取，以及在探索性数据分析中寻找数据中的模式。\n8.1 PCA的作用 降维：减少数据的维度，降低计算复杂性，同时保留最重要的信息。\n去相关：新的特征（主成分）是线性无关的，这有助于消除原始数据中的多重共线性问题。\n数据压缩：通过保留最重要的主成分，可以有效地压缩数据。\n可视化：在高维数据集中，PCA 可以帮助将数据投影到二维或三维空间，以便于可视化。\n噪声过滤：PCA 可以通过去除数据中的噪声来提高模型的性能。\n数据预处理：在许多机器学习算法中，PCA 可以作为数据预处理步骤，以提高算法的效率和准确性。\n8.2 PCA的使用 PCA的使用过程可以分为 步。假设我们拥有数据集X，则需按如下步骤进行：\n1.中心化：将数据集中的每个特征减去其均值，使得新的数据集具有零均值。从视觉上看，这就像是将点移到了坐标轴中间。\n假设我们有一个数据集X，其中包含n个样本和m个特征。中心化的过程可以表示为：\n$$ \\mu = \\begin{pmatrix} \\frac{1}{n} \\sum_{i=1}^{n} X_{i1}\\frac{1}{n} \\sum_{i=1}^{n} X_{i2} \\\\ \\vdots \\\\ \\frac{1}{n} \\sum_{i=1}^{n} X_{im} \\end{pmatrix} $$ $$ X_{\\text{centered}} = X - \\mu $$ 2.协方差矩阵:计算中心化数据的协方差矩阵，以了解特征之间的关系。\n协方差：协方差可以方便的度量数据与数据之间对于彼此的变化趋势。公式表示如下。其中 μ 是X，Y的均值。E是期望值。\n$$ Cov(X i ​ ,X j ​ )=E[(X i ​ −μ i ​ )(X j ​ −μ j ​ )] $$ 方差：方差可以方便的衡量数据在轴上的密集程度。方差定义为定义为该随机变量与其均值（期望值）之差的平方的期望值。公式表示如下：\n$$ Var(X)=E[(X−μ) 2 ] $$ 通过协方差和方差来定义协方差矩阵，定义如下：\n$$ \\text{Cov}(x, y) = \\frac{1}{n-1} \\sum_{i=1}^{n} (x_i - \\mu_x)(y_i - \\mu_y) $$ $$ C = \\text{Cov}(X) = \\begin{bmatrix} \\text{Var}(X_1) \u0026 \\text{Cov}(X_1, X_2) \u0026 \\cdots \u0026 \\text{Cov}(X_1, X_n) \\\\ \\text{Cov}(X_2, X_1) \u0026 \\text{Var}(X_2) \u0026 \\cdots \u0026 \\text{Cov}(X_2, X_n) \\\\ \\vdots \u0026 \\vdots \u0026 \\ddots \u0026 \\vdots \\\\ \\text{Cov}(X_n, X_1) \u0026 \\text{Cov}(X_n, X_2) \u0026 \\cdots \u0026 \\text{Var}(X_n) \\end{bmatrix} $$ 3.特征值分解：\n在已经计算出协方差矩阵的前提下，找出协方差矩阵的特征值和特征向量（被称为主成分）。因为协方差矩阵的转置不变的特点，所以特征向量必然正交。 特征值和特征向量计算的过程在前面有涉及。 4.选择主成分：根据特征值的大小选择最重要的特征向量，特征值越大，对应的特征向量越重要。这些特征向量构成了新的特征空间。\n根据我们要的目标空间的维度，选择对应数量的特征向量。优先选择特征值大的特征向量作为投影对象。 5.转换数据：将数据投影到对应的空间上，完成PCA。\n投影的过程如下：\n乘以目标空间的张成向量可以投影到目标空间上，而除以目标空间张量的范数可以避免发生延展。\n$$ A_P = A \\frac{v}{\\|v\\|_2} $$ 8.3 PCA的局限性 线性假设：PCA 假设数据的主成分是线性的，对于非线性结构可能不适用。 对异常值敏感：PCA 对异常值非常敏感，异常值可能会对主成分产生较大影响。 9. 总结 线性代数作为数学的一个重要分支，在人工智能（AI）领域的应用至关重要。从基础的矩阵运算到复杂的线性变换，线性代数的概念和工具为AI算法的开发和优化提供了坚实的理论基础和计算框架。\n7.1 线性代数的核心作用 线性代数的核心作用体现在以下几个方面：\n理论基础：线性代数为AI算法提供了理论支撑，使得算法的实现成为可能。例如，神经网络中的权重更新和反向传播算法依赖于矩阵和向量的运算。\n数据处理：在AI中，数据通常以矩阵的形式表示，线性代数提供了处理和分析这些数据的有效工具，如特征提取和降维。\n模型优化：线性代数在模型优化中扮演着关键角色，尤其是在优化算法中，如梯度下降法，需要用到微积分和线性代数的知识。\n算法创新：许多新的AI算法，如卷积神经网络（CNN）和循环神经网络（RNN），都是基于线性代数的理论创新。\n7.2 线性代数的实际应用 线性代数的实际应用包括但不限于：\n图像处理：在计算机视觉中，线性变换用于图像的旋转、缩放和剪切等操作。\n自然语言处理：在线性代数的帮助下，可以实现词向量的转换和文本的特征提取。\n优化问题：在线性代数的支持下，可以求解优化问题中的线性方程组，如在支持向量机（SVM）的训练过程中。\n特征工程：线性代数提供了特征归一化和相似性度量的工具，这对于特征选择和降维非常重要。\n[NOTE]\n本篇存在一定AI辅助\n","date":"2025-01-01T00:00:00Z","image":"https://SJTdreams.github.io/p/%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0%E7%BA%BF%E6%80%A7%E4%BB%A3%E6%95%B0%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/title_hu_370df9105be6564b.png","permalink":"https://SJTdreams.github.io/p/%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0%E7%BA%BF%E6%80%A7%E4%BB%A3%E6%95%B0%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/","title":"机器学习线性代数学习笔记"},{"content":"更新日志 2024.12.29 博客创建完毕 2025.1.1 博客同步和发布完毕，基本功能齐全 2025.1.2 加入标签云，加入画廊，加入音乐播放器 ","date":"2024-12-29T12:41:22+08:00","image":"https://SJTdreams.github.io/p/%E6%9B%B4%E6%96%B0%E6%97%A5%E5%BF%97/title_hu_96dbee2bc1f4ee90.jpeg","permalink":"https://SJTdreams.github.io/p/%E6%9B%B4%E6%96%B0%E6%97%A5%E5%BF%97/","title":"更新日志"}]