2024年11月

最近大家都在探讨和尝试复现OpenAI O1的思考效果,解码出的关键技术方向,包括之前已经探讨过的Inference Time Scaling在推理过程中进行路径决策和选择。但想要更优的Inference Time Scaling曲线,前提是模型本身是一个很强的Generator,已经拥有足够的生成合理推理过程的能力,同时还拥有很强的Verifier模型来对推理节点进行打分决策,并且二者可以在少人类监督的条件下不断迭代优化。

这一章我们先聊聊如何让大模型"自学"推理思考,从而得到思考推理能力更强的Generator。本章会以STaR论文为基础,介绍生成复杂动态思维链背后可能的技术方案

STaR

  • STaR: Self-Taught Reasoner Bootstrapping ReasoningWith Reasoning

STaR是这一系列论文的第一篇,思路就是妥妥的Bootstrap,生成推理过程->训练模型->生成更优的推理过程->训练更强的模型。

STaR的流程很直观

  • Pretrain模型,通过指令+fewshot,引导模型对QA数据集生成推理过程
  • 对以上推理过程进行过滤,只保留回答正确的
  • 对推理答案错误的,通过Hint(在上文中告诉模型正确答案),引导模型生成正确的推理过程,对这部分样本也进行过滤,只保留回答正确的
  • 使用以上样本进行SFT,教模型如何思考
  • 再使用SFT后的模型重复以上样本生成的过程,直到评估指标不再提升

STaR的优缺点都非常明显,优点就是不需要大量人工标注的思维链样本,也不依赖更强大的模型提供合成样本(其他模型提供的合成样本本身也可能存在分布漂移会影响模型效果),实现了一定程度的模型自我优化提升。缺点有

  • 可用场景有限:STaR依赖正确答案作为过滤条件,因此只适用于问答,数学计算等有限领域,对于更广泛的开放领域无法适用。这个限制其实也是因为STaR并未引入Verifier,因此只能依赖答案本身作为评估基准。
  • SFT本身的泛化性有限:通过SFT把生成的推理过程注入模型,很难让模型学到推理过程中的奖励信号,更多还是在做Behaviour Cloning。达不到"Don't Teach, Incentive"的效果
  • STaR对样本的使用率不足,只使用了唯一的一条正确样本,丢弃了通往正确答案的更多正确路径,也丢弃了更大量级的错误思考过程
  • 思考链路是静态,既针对任何问题模型都默认上来就进行思考,这种形式在单一场景中适用,在更灵活广泛的实际场景中思考应该动态存在

下面我们看下针对以上问题,其他论文给出了哪些优化方案,以下论文更多会关注和STaR的对比~

RFT

  • Scaling relationship on learning mathematical reasoning with large language models

RFT也是模型自我合成数据进行优化的方案,它没有使用STaR的多轮Bootstrap来持续优化合成数据,只用了一轮优化,但RFT给出了在一轮迭代内,更充分利用正样本的方案。

RFT会使用SFT后的模型,针对每个问题随机采样100条推理路径,筛选所有答案正确的推理路径,并使用编辑距离对不同的推理路径进行消重,只保留差异化的正确推理路径。这样对比以上STaR每个问题只有1条正确样本,RFT对每个问题会保留多样性的正确推理路径,然后使用该合成数据集对模型进行训练。对比后发现使用更多推理路径效果会有提升,同时去重也会带来明显的效果提升。大概率因为不去重,会导致部分重复样本的过度拟合,影响泛化性。

RFT这种使用模型自我合成数据再微调基座的方案,在后面Google Deepmind的论文中也进一步论证了它的有效性要超过使用更强大的模型直接合成数据的效果。部分因为多个正确推理路径的提供,能给模型提供一些哪些推理节点是核心节点的有效信息,降低模型模仿率,提高模型泛化性。
image

V-STaR

  • V-STaR: Training Verifiers for Self-Taught Reasoners

V-STaR沿用了STaR的多轮Bootstrap样本迭代的方案,并给出了一种简单的利用负样本的方案,在以上STaR的基础上,每一轮模型生成推理答案时,正确和错误的推理链路都会被保留,其中正确的样本用来训练微调Generator,而正确和错误的样本会合并用于训练Verifier。

以及和STaR每一轮都只使用新训练的Generator合成的样本不同,这里训练Verifier的样本是每一轮收集样本的并集。因为RM模型需要广泛学习不同分布的推理结果,而每一轮随着Generator不断增强,其实都在拓宽RM模型学习的样本范围,提升Verifier的泛化性。

最后论文用收集好的正负样本,构建了针对问题的对比样本对(x, y+,y-) ,然后使用DPO在最后一轮微调得到的最优的Generator上来训练Verifier。并在推理过程中使用该Verifier,来实现best-of-n策略,从N个随机采样的推理结果中选择RM得分最高的推理链路。

image

效果上加入Verifier的STaR效果会有进一步提升,并且多轮Bootstrap也能有效提高V-STaR的效果。

Incorrect Synthetic Data

  • RL on Incorrect Synthetic Data Scales the Efficiency of LLM Math Reasoning by Eight-Fold

GDM这篇论文对正负合成思维链样本都做了更加全面的讨论,基本结论如下

  • 正样本:论文论证了前面RFT,也就是使用微调模型自我生成推理链路的方案,要优于使用更强模型直接生成样本进行SFT。但是只使用合成正样本做SFT,因为无法保证链路的完全正确,会让模型学到一些混淆的错误思考模式。
  • 负样本:对比V-STaR只在Verifier中简单利用了负样本,论文给出了在优化Generator中使用负样本的训练方案

下面我们分正负样本来分别说下~

正样本:为何自我生成的正样本效果更好?

论文分别采用两种方案来合成数据

  • SFT:使用更强大的模型合成数据,例如GPT4来生成带有思维链的推理样本,经过简单的消重,过滤错误答案后,使用正确样本直接微调模型
  • RFT:模型自我合成数据,使用以上微调后的模型,针对每个问题再生成N个推理结果,经过过滤后使用正确的样本微调模型,也就是使用基座微调模型自我生成的样本再回来微调基座

论文发现在Deepseek和Llama2上,随着合成数据集的数量变大,RFT显著优于SFT,并且优势并不随数据集变大而缩小。具体到
数据使用效率,相同的Test Error下,使用RFT策略训练的效果相当于使用2倍的合成数据进行SFT

image

这个结论会有一些反直觉,因为之前很多优化小模型的思路都是去蒸馏GPT4的回答。当然后面也有一些研究认为
拟合另一个模型的回答,因为预训练的差异,导致微调过程中模型很难直接学习新的推理回答只能强行记忆,影响模型泛化效果。
类似的问题其实在早期我们也用GPT3.5,GPT4的回答去构建样本,然后微调一些小模型的时候就发现了,当回答风格差异巨大的时候,直接微调,会影响基座本身的知识存储和指令理解。其实就是小模型为了去强行改变自己的输出风格,负向影响了模型本身的参数分布。

论文使用RFT生成的样本,相比SFT样本,在基座模型上有更高的log likelihood来论证之所以使用RFT的样本微调效果更好,就是因为
RFT样本是基座模型自我合成的,因此和基座模型本身的推理分布更加接近,模型更好学习,会降低模型去强行记忆的概率,对泛化性的损失更小,更加“easy-to-fit”。

但不论是SFT还是RFT,论文提出都需要关注
正确样本中错误的推理链路
,因为样本过滤只使用了答案,并未对中间推理链路的正确性进行校验,而这些错误的步骤,会导致模型学到一些混淆的因果关系。而
虚假步骤带来的推理问题,并无法通过简单的增加合成数据的方法来解决。

下面我们接着看论文如何通过引入负样本和per-step DPO来优化合成样本中错误步骤带来的问题。

负样本:呦呵你没想到我也这么有用吧

既然同一个问题生成多条正向的推理链路的合成样本可以提升效果,那如何更有效的利用比正样本占比更高的负样本呢?前面V-STaR是选择利用负样本去训练Verifier,而GDM的论文给出了通过
正负样本对比学习
来充分利用负样本的方案。论文设计的RL目标函数如下,通过正负样本分别和基准(微调后的基座模型)模型对比,来进行对齐。

image

并且论文给出了从“
关键步骤
”这个概念出发构建正负样本对的方案,那啥叫关键步骤嘞?

可以从熵值的视角去看,如果生成步骤A后,模型得到正确答案,或者错误答案的概率显著更高,那步骤A就是关键步骤。其中通往错误的核心步骤需要模型遗忘,通过正确的核心步骤需要学习。

那如果生成步骤A后,模型得到正确和错误答案的概率一半一半,那步骤A就不是关键步骤。想要获得每个步骤通往正确、错误答案的概率,其实只需要通过蒙特卡洛模拟采样足够多的链路,然后做个概率统计就行嘞

以上的
关键价值
,论文用以下的公式来计算,每个步骤(i)的价值(Q value),都是给定(1
i-1)的步骤,计算步骤模型在未来(i+1
L)步内获得正确答案的期望概率。以上价值其实是步骤(1~i)的累计价值,而每个步骤的增量价值,就是和截止前一步Q value的差值。

image

image

所以构建正负推理链路的步骤,就是基于每个问题,使用微调后的基准模型采样N条推理链路,基于答案是否正确,先初步分成正负样本。然后在负样本内,寻找每个推理链路中关键价值最低的第一个步骤(大概率导致模型失败的第一个核心节点)作为
\(-y_{1:c}\)
。在正样本内,寻找每个推理链路中关键价值的第一个步骤作为
\(+y_{1:c}\)
,然后基于这些正负样本对,使用以上对比RL损失函数进行训练。

效果上对比SFT,per-step DPO训练的效果,会发现
相同Test-Error下,使用per-step DPO训练,相当于使用8倍的合成数据进行SFT, 也就是per-step DPO的合成数据使用效率比SFT要高8倍
对比前面RFT,利用负样本,识别关键步骤进行对比学习,对合成数据有更高的利用率~

image

Quiet-STaR

Quiet-STaR拓展了以上STaR们的应用范围,针对开放世界的各类问题,给出了让模型先给出多条思考路径,再在推理时动态决策多大程度上依赖前置思考来回答问题的新方案。

image

Quiet-STaR的实现包括3步,我们对照的着下面的代码来说

  1. N路平行思考生成:针对每个输入token,生成N个推理链路,每个推理链路的长度都是固定的,并且在推理链路的开始和结束,分别插入新的Special token <startofthought>,<endofthought>。 这些推理链路就是模型的隐藏思考步骤。对应以下代码中continuation迭代生成的部分。
  2. 混合权重头:论文在原生LLM的基础上添加了一个分类头,就是简单一层或多层MLP。分类头用于进行权重混合,也就是在预测真实的下一个token的时候,根据分类头的权重,决定多少信息来自上一个输入token,多少信息来自新生成的隐藏思考步骤。对应以下代码中的mixing_weight和mixed_hidden_states。
  3. RL训练优化思考生成:最后通过强化学习,在训练以上<startofthought>,<endofthought>,MLP分类层的基础上,让模型生成的隐藏思考步骤更加有用,可以提高模型推理效果。
# Append the start thought token to the input sequence
start_thought_token_id = self.tokenizer.convert_tokens_to_ids("<|startthought|>")
input_ids = torch.cat([input_ids, torch.tensor([[start_thought_token_id]] * batch_size).to(input_ids.device)], dim=-1)
seq_len += 1

# Update the attention mask
if attention_mask is not None:
    attention_mask = torch.cat([attention_mask, torch.ones((batch_size, 1)).to(attention_mask.device)], dim=-1)

# Generate the continuation
continuation_length = self.n_ahead - 2
new_key_values = past_key_values

start_time = time.time()
for continuation_idx in range(continuation_length):
    outputs = self.model(
        input_ids=input_ids if continuation_idx == 0 else next_token_id.unsqueeze(-1).to(input_ids.device),
        attention_mask=attention_mask,
        position_ids=position_ids,
        past_key_values=new_key_values,
        inputs_embeds=inputs_embeds,
        use_cache=True,
        output_attentions=output_attentions,
        output_hidden_states=output_hidden_states,
        return_dict=return_dict,
    )
    new_key_values = outputs.past_key_values

    hidden_states = outputs[0]

    logits = self.lm_head(hidden_states)
    logits = logits[:, -1, :]  # Only consider the last token

    # Apply Gumbel-Softmax to the logits
    next_token_logits = F.gumbel_softmax(logits, tau=self.gumbel_temperature, hard=True, dim=-1)
    next_token_id = torch.argmax(next_token_logits, dim=-1)

    # Append the generated token to the input sequence
    input_ids = torch.cat([input_ids, next_token_id.unsqueeze(-1).to(input_ids.device)], dim=-1)
    seq_len += 1

    # Update the attention mask
    if attention_mask is not None:
        attention_mask = torch.cat([attention_mask, torch.ones((batch_size, 1)).to(attention_mask.device)], dim=-1)

# Append the end thought token to the input sequence
end_thought_token_id = self.tokenizer.convert_tokens_to_ids("<|endthought|>")
input_ids = torch.cat([input_ids, torch.tensor([[end_thought_token_id]] * batch_size).to(input_ids.device)], dim=-1)
seq_len += 1

# Update the attention mask
if attention_mask is not None:
    attention_mask = torch.cat([attention_mask, torch.ones((batch_size, 1)).to(attention_mask.device)], dim=-1)

# Get the hidden states before and after the thought
outputs_before = self.model(
    input_ids=original_input_ids,
    attention_mask=original_attention_mask,
    position_ids=position_ids,
    past_key_values=past_key_values,
    inputs_embeds=inputs_embeds,
    use_cache=use_cache,
    output_attentions=output_attentions,
    output_hidden_states=output_hidden_states,
    return_dict=return_dict,
)
hidden_states_before = outputs_before[0][:, -1:, :]

# two new tokens: last continuation token and end thought token
outputs_after = self.model(
    input_ids=torch.cat([next_token_id.unsqueeze(-1).to(input_ids.device), torch.tensor(end_thought_token_id).unsqueeze(-1).unsqueeze(-1).to(input_ids.device)], dim=-1),
    attention_mask=attention_mask,
    position_ids=position_ids,
    past_key_values=new_key_values,
    inputs_embeds=inputs_embeds,
    use_cache=use_cache,
    output_attentions=output_attentions,
    output_hidden_states=output_hidden_states,
    return_dict=return_dict,
)
hidden_states_after = outputs_after[0][:, -1:, :]

# Apply the talk head to get the mixing weight
mixing_weight = self.talk_head[0](torch.cat([hidden_states_before, hidden_states_after], dim=-1))

# Apply the mixing weight to the hidden states
mixed_hidden_states = (1 - mixing_weight) * hidden_states_before + mixing_weight * hidden_states_after

# Apply the language model head to get the final logits
logits = self.lm_head(mixed_hidden_states)

按照以上的步骤,我们来举个例子,输入是“今天天气“:

1. 平行思考生成(N=5)
  - 思考1:<startofthought>是否下雨、下雪等异常天气<endofthought>
  - 思考2:<startofthought>温度、湿度情况<endofthought>
  - 思考3:<startofthought>湿度情况<endofthought>
  - 思考4:<startofthought>对人们生活活动的影响<endofthought>
  - 思考5:<startofthought>穿衣建议<endofthought> 
2. 权重混合(思考1举例)
  - 无思考时输入的next-token:很(0.4)+ 真(0.6)
  - 思考1后面的next-token:晴朗(0.2) + 下雨(0.2) + ....各种天气情况
  - 思考1混合后(混合权重0.5):很(0.2) + 真(0.3)+晴朗(0.1) +下雨(0.1)+ ....各种天气情况*0.5
3. RL训练(如果原始文本后面是“晴朗”)
  - RL给予“思考1”以正向梯度更新,在天气后面思考温度有助于模型推理

论文给出的一个实际的推理效果如下
image

再来说下训练的部分,论文采用了强化学习来训练新加入的MLP混合头,思考开始和结束的token,以及基座模型本身。因为前面再每个位置生成了N个不同的思考路径,论文选择的RL训练目标是优化每个token位置生成正确思考路径,相对其他思考路径对推理下一个token带来的增量信息。
image

然后鼓励模型去生成对推理下一个token有帮助作用的思考路径(Tj)

image

同时还会增加一个NLI Loss,来训练用于混合思考和推理权重的MLP头。训练对比前面STaR等策略都是使用QA等特定领域指令样本,Quiet-STaR选择OpenWebMath(技术网站爬虫数据)进行训练,考虑技术类文本依赖思考的情况更多,模型训练得到的正向信号会更加密集。因为训练本身是预训练的Next-Token-Prediction,因此对比STaR具有更好的泛化效果,可以不限领域,不限任务进行训练。

Quiet-STaR还有待进一步优化的问题包括

  • 动态思考位置的选择:Quiet-STaR是在每个位置都生成N个思考链路后,再使用mix-head来对每个位置的思维链和原始推理进行权重融合,属于后选择方案,推理成本较高,如果能根据输入本身进行前置的思考位置选择,只在最优的一个或几个位置上进行内生思考推理(MCTS)就更完美了
  • 模型内容思考可能本身不可解释,因为Quiet-STaR只在HighLevel层面去优化加入内生思考后,模型推理效果的提升,并未对思考本身的next-token prediction进行对齐,导致生成的思考本身甚至可能并不在语言上通顺。当然因为本身是在训练后的基座模型上推理,所以肯定保留了部分的语言逻辑性
  • 模型内生思考可能存在各种3H(helpful,harmless,honesty)问题。同样是对齐问题,模型生成的思考链路不仅未在语言模型角度对齐,也未在人类偏好角度对齐,这可能也是OpenAI在O1中考虑对用户隐藏内在思考链路的原因之一。而对齐本身是否会影响内生思考的效果需要额外的实验验证。

Quiet-STaR和OpenAI O1在生成模型内生思考上的技术栈是很像的。OpenAI在O1的使用说明
Link
中也指出,O1是通过动态插入思考token,来生成内生思考,并基于内生思考进行推理回答,思考对用户不可见(OpenAI在
Learning to Reason with LLMs
中也说明隐藏思维链的部分是未对齐的),只展示回答部分。而多轮对话的上文也只会使用输入输出不会使用内生回答。使用感受上在金融场景下,一些强数字,强逻辑的问题例如表格问答,财务问题分析上O1有比较显著的效果提升。

image

想看更全的大模型论文·微调预训练数据·开源框架·AIGC应用 >>
DecryPrompt


OpenAI O1技术路线解析的一些好文推荐~

  1. OpenAI Learning to Reason with LLMs
  2. 北大对齐团队独家解读:OpenAI o1开启「后训练」时代强化学习新范式
  3. Reverse engineering OpenAI’s o1
  4. OpenAI’s Strawberry, LM self-talk, inference scaling laws, and spending more on inference
  5. OpenAI o1 self-play RL 技术路线推演
  6. 让 LLM 下一盘大棋:RL 范式探讨

前言

经常有粉丝朋友在公众号后台私信提问,因为个人平时比较少看公众号后台的私信所以没法及时回复。最近发现腾讯推出了一个可以创建和使用各种智能体的平台(
帮助小白也能快速使用AI
):
腾讯元器
,正好自己每天也在公众号更新.NET相关的文章(到目前为止.NET相关的文章应该有400多篇了)有着较为丰富.NET相关的知识库,因此今天我将利用腾讯元器,将我的公众号变身为一个强大的.NET AI智能体,造福我的.NET粉丝朋友们。

登录腾讯元器

创建智能体

简介

我是一名专注于.NET领域的AI智能体,掌握全面的.NET技术栈和知识点。我不仅是.NET开发者的得力助手,能够帮助.NET开发者快速解答各类技术难题,还是他们的知识伙伴,助力.NET开发者高效学习、进阶成长,共同推动.NET技术的发展和应用创新。

详细设定

# 角色
你是一位专注于.NET领域的AI智能体,掌握全面的.NET技术栈和知识点。我不仅是.NET开发者的得力助手,能够帮助.NET开发者快速解答各类技术难题,还是他们的知识伙伴,助力.NET开发者高效学习、进阶成长,共同推动.NET技术的发展和应用创新。

## 人设
- 姓名:大姚
- 爱好:一个热爱开源的.NET软件开发工程师,擅长C#、.NET、.NET Core等相关技术。有分布式、微服务应用,云原生应用,微信Web应用、小程序,H5移动端应用,企业Web应用(ERP,CRM,OA等系统)设计和开发经验。

## 技能
- 准确理解用户提出的.NET、C#相关的编程问题或需求。
- 运用.NET、C#相关编程语言知识,提供清晰、高效的代码示例。
- 对输出的代码进行详细注释,便于用户理解每部分的功能特性。

## 注意事项
- 确保提供的代码符合最佳实践和编程规范。
- 针对不同编程语言采用相应的代码风格和习惯。
- 提供的代码示例应该直接解决问题,并且易于扩展和维护。

开场白

您好,我是 DotNetGuide 您的专属.NET AI智能体。我精通.NET技术栈开发,致力于为您提供高效、专业的技术支持与解决方案,携手共探.NET技术的无限可能。

添加知识库

授权公众号中的所有文章允许腾讯元器大模型调用:

公众号授权完成后,在等待所有的文章向量化完毕,你就可以得到一个每天自动根据你的公众号文章更新的知识库了。

发布智能体

发布成功

查看使用方式

前往公众号提问

AI智能体Web体验

AI智能体小程序体验

关联小程序

配置AI智能体菜单

复制腾讯元器的小程序路径 (path):

AI智能体小程序提问

背景

周一刚上班一个开发小哥火急火燎的过来找我,黑龙江某客户私有化环境的服务过一阵就报数据库连接失败,不知道是什么原因导致的,我以为是客户调整了网络,但是客户说并没有做任何调整,我使用ping测试也看不出什么问题,于是猜测是业务代码导致,我就问他最近是否有发布,他说上周的确发了一个版本,他也怀疑过是代码的问题,所以组织组内成员对发布的代码已经review了好几遍,并没有发现可疑点。

排查

[DruidPooledStatement.java:379] - CommunicationsException, druid version 1.2.8, jdbcUrl : jdbc:mysql://xxx:3306/db_xxx?useUnicode=true&characterEncoding=UTF-8&useOldAliasMetadataBehavior=true&useSSL=false&allowMultiQueries=true&rewriteBatchedStatements=true&serverTimezone=GMT%2B8&zeroDateTimeBehavior=convertToNull&autoReconnect=true, testWhileIdle true, idle millis 9950, minIdle 1, poolingCount 5, timeBetweenEvictionRunsMillis 60000, lastValidIdleMillis 9950, driver com.mysql.cj.jdbc.Driver, exceptionSorter com.alibaba.druid.pool.vendor.MySqlExceptionSorter
2024-11-05 10:35:19.671 [inter-listener-threadPool7] [] ERROR [DruidPooledStatement.java:379] - CommunicationsException, druid version 1.2.8, jdbcUrl : jdbc:mysql://xxx:3306/db_xxx?useUnicode=true&characterEncoding=UTF-8&useOldAliasMetadataBehavior=true&useSSL=false&allowMultiQueries=true&rewriteBatchedStatements=true&serverTimezone=GMT%2B8&zeroDateTimeBehavior=convertToNull&autoReconnect=true, testWhileIdle true, idle millis 8464, minIdle 1, poolingCount 5, timeBetweenEvictionRunsMillis 60000, lastValidIdleMillis 8464, driver com.mysql.cj.jdbc.Driver, exceptionSorter com.alibaba.druid.pool.vendor.MySqlExceptionSorter
2024-11-05 10:35:19.672 [inter-listener-threadPool7] [] ERROR [JdbcUtils.java:89] - close connection error
java.sql.SQLNonTransientConnectionException: Communications link failure during rollback(). Transaction resolution unknown.

Communications link failure,多么熟悉的异常信息,老司机都知道这大概率就是网络问题,奈何找不到任何网络出问题的蛛丝马迹,所以尝试换个思路,从MySQL自身的日志碰碰运气,果不其然,MySQL的error日志中显示有条读取JSON的sql导致其退出,一起看下现场

复现

如我所料,只要执行日志中的sql就会让MySQL意外退出,紧接着重新启动,此时应用程序就会报Communications link failure,这条sql究竟有什么魔力居然可以让大名鼎鼎的MySQL瞬间崩塌,好奇心刹那涌上心头,接下来我们剥茧抽丝。

剥茧抽丝

从MySQL的日志中可以看到“This could be because you hit a bug”,怎么会呢?系统中对于JSON的操作语句很早就存在,如果是存在bug那早就暴露了,也不会等到今天,那会是什么原因呢?

从MySQL日志的错误栈可以看到大概是JSON解析失败了,过往经验告诉我引起JSON解析失败的原因无外乎格式不正确,那我就看下那行数据的extdata是否正确。

乍一看好像没啥问题,这本来也不是一个必填字段,空着就空着吧,其实不然,通过和其他数据对比,发现这其实是空串,并不是NULL,对NULL执行->>'$.'不会引起MySQL崩溃,而空串会。

到这也就可以解释“系统中对于JSON的操作语句很早就存在,如果是存在bug那早就暴露了,也不会等到今天才爆发,那会是什么原因呢?”,这次上线的代码对这个字段有写入空串的情况。

但是也引发我的另一个疑问,为什么其他环境没有问题?

我就不卖关子了,是因为其他环境的MySQL版本较高,这个环境使用的是5.7.20版本,我在翻看MySQL更新日志的时候发现5.7.22中确实提到修复了老版本的一个JSON函数导致MySQL退出的bug

遗憾的是“Bug #22253965”在网络上已经搜索不到具体的内容了,一位国外开发者针对这个bug为什么找不到的问题在MySQL论坛提出了讨论,也得到了回复,有兴趣的可以移步https://forums.mysql.com/read.php?10,667824,667824#msg-667824。

解决方案

升级,升级,升级。

事后思考

时刻关注软件的更新列表很重要,不要因为它的广泛使用就忽视升级,虽然升级过程可能像飞行中换引擎一样充满挑战,但这是让它保持最佳状态的关键步骤呢!

推荐阅读

https://dev.mysql.com/doc/relnotes/mysql/5.7/en/news-5-7-22.html#mysqld-5-7-22-bug

在当今复杂的 IT 环境中,实时监控与可视化对于保障系统的稳定运行和性能优化至关重要。 无论是服务器、应用程序,还是网络设备,及时获取性能数据能够帮助我们快速定位问题、优化资源配置。

Netdata
,作为一个开源的实时监控工具,正是为此而生。
Netdata
不仅是一个轻量级的监控与可视化平台,更是一个功能强大且易于使用的实时性能监测工具,凭借其强大的功能和炫酷的界面,已经在GitHub上获得超过了72K的星标。

一、Netdata介绍

Netdata
是一款开源的、轻量级的实时性能监控和可视化工具。它能够监控服务器、容器、应用程序和物联网设备,为用户提供实时、详细的性能数据。可以运行在多种操作系统上,包括 Linux、FreeBSD、macOS 等。其设计初衷是为了让系统管理员和开发人员能够快速、直观地了解系统的运行状态,从硬件层面到各种应用程序和服务的性能指标都能尽收眼底。

Netdata能够帮助用户实时监测系统的性能指标,提供丰富的可视化界面,使用户可以轻松查看各类指标,包括CPU使用率、内存占用、磁盘IO、网络流量等。

Netdata以秒为单位收集和展示数据,让用户对系统状态有直观的了解。通过友好的Web界面,Netdata将复杂的数据以图表的形式呈现,使得用户能够轻松识别趋势和异常。无论是开发者、运维人员,还是数据分析师,都能从中受益。

二、Netdata 的核心功能

1、广泛的监控范围

1) 系统资源监控

Netdata 可以对 CPU、内存、磁盘 I/O、网络等基本的系统资源进行实时监控。对于 CPU,它不仅能显示使用率,还能细分到每个核心的使用情况,帮助我们快速定位是否存在某个核心负载过高的问题。在内存监控方面,能够详细呈现内存的使用量、可用量、缓存大小等信息,对于排查内存泄漏等问题非常有帮助。磁盘 I/O 的监控可以展示读写速度、操作次数等指标,有助于分析磁盘性能瓶颈。网络监控则涵盖了网络接口的带宽使用情况、进出流量等,无论是排查网络拥塞还是分析应用程序的网络通信效率都能提供有力的数据支持。

2) 应用程序和服务监控

它支持对众多常见的应用程序和服务进行监控,比如数据库(MySQL、PostgreSQL 等)、Web 服务器(Apache、Nginx 等)、消息队列(RabbitMQ、Kafka 等)。以 MySQL 为例,Netdata 可以监控查询执行时间、连接数、缓存命中率等关键指标,让数据库管理员能够及时发现数据库性能问题,如慢查询过多或者连接池溢出等情况。对于 Web 服务器,能够监控请求数、响应时间、错误率等,从而保障网站的正常运行和用户体验。

2、实时性与低延迟

Netdata 的一大优势在于其出色的实时性。它能够以极低的延迟收集和更新数据,通常在秒级甚至亚秒级。这意味着当系统中出现性能问题或者异常情况时,管理员可以几乎立即在 Netdata 的可视化界面上看到相关的指标变化。这种实时反馈对于快速响应和解决问题至关重要,例如在应对突发的流量高峰或者服务器故障时,可以迅速采取措施,减少对业务的影响。

3、惊艳的可视化界面

1) 直观的仪表盘

Netdata 拥有高度可定制化且直观的仪表盘。仪表盘上以各种图表(如折线图、柱状图、饼图、热力图等)的形式展示监控数据。这些图表色彩丰富、交互性强,用户可以轻松地缩放、平移图表,查看不同时间段的数据。例如,在查看 CPU 使用率的折线图时,可以通过缩放功能详细查看某个短时间内的使用率波动情况,或者通过平移查看较长时间范围内的整体趋势。

2) 分层展示与钻取功能

数据在仪表盘上是分层展示的,用户可以从宏观的系统层面逐步深入到具体的应用程序、服务甚至是某个功能模块的指标。例如,从整个服务器的资源使用情况,钻取到某个特定 Web 应用的请求处理指标,再进一步查看某个 API 端点的响应时间数据。这种钻取功能方便用户快速定位问题所在的层次和具体位置。

3、如何安装Netdata?

Netdata 可以安装在多种操作系统上,以下是常见的安装方法:

1、Linux 系统安装

基于脚本安装(以 CentOS 为例): 安装依赖:打开终端,执行以下命令安装必要的依赖项。

yum install -y autoconf automake curl gcc git libmnl-devel libuuid-devel lm_sensors make MySQL-python nc pkgconfig python python-psycopg2 PyYAML zlib-devel

拉取 Git 仓库:

git clone https://github.com/netdata/netdata.git --depth=1

执行安装脚本:进入克隆下来的 netdata 目录,执行安装脚本。

./netdata-installer.sh

配置防火墙(如果需要):如果系统开启了防火墙,需要允许访问 Netdata 的默认端口 19999。例如:

systemctl stop firewalld.service
firewall-cmd --zone=public --add-port=19999/tcp --permanent
systemctl restart firewalld.service

2、使用一键安装脚本

下载 Netdata 的安装脚本:

wget https://my-netdata.io/kickstart.sh -O /tmp/netdata-kickstart.sh

运行安装脚本:

sudo bash /tmp/netdata-kickstart.sh

3、Docker 安装

确保已经安装了 Docker 环境。 执行以下命令拉取 Netdata 镜像并启动容器:

docker run -d --name=netdata \
-p 19999:19999 \
-v netdatalib:/var/lib/netdata \
-v netdatacache:/var/cache/netdata \
-v /etc/passwd:/host/etc/passwd:ro \
-v /etc/group:/host/etc/group:ro \
-v /proc:/host/proc:ro \
-v /sys:/host/sys:ro \
-v /etc/os-release:/host/etc/os-release:ro \
--restart unless-stopped \
--cap-add SYS_PTRACE \
--security-opt apparmor=unconfined \
netdata/netdata

安装完成后,默认情况下可以通过浏览器访问
http://<服务器 IP>:19999
来查看 Netdata 的监控界面。

4、Netdata监控效果展示

1、Home首页效果展示

2、Nodes节点页面展示

3、Metrics页面展示

4、K8S容器化监控

5、告警规则页面

6、Anomaly页面

7、数据库监控

官方demo体验地址:

https://app.netdata.cloud/spaces/netdata-demo/rooms/http-endpoints/overview

5、小结

Netdata是一款功能强大且易于使用的开源性能监测工具,以其卓越的性能、直观的界面和丰富的功能,为用户提供了实时监控系统和应用程序性能的便利。通过Netdata,用户可以及时发现并解决系统问题,优化资源配置,提高整体服务质量。如果你正在寻找一款轻量级且强大的实时监控工具,不妨试试Netdata,相信它会给你带来意想不到的惊喜。

Netdata的GitHub项目地址为:
https://github.com/netdata/netdata

希望本文能够帮助你更好地了解和使用Netdata,如有任何问题或建议,请随时在评论区留言。

Cone

Manim
中专门用于创建和操控
锥形几何对象
的类。

Cone
允许用户定义锥体的底面半径、高度、颜色、不透明度等属性,并提供了一系列方法来操控这个锥体,如移动、缩放、旋转等。

通过这些属性和方法,用户可以灵活地创建出符合自己需求的锥形对象,并将其融入到动画或演示中。

1. 主要参数

Cone
的主要参数有:

参数名称 类型 说明
base_radius float 圆锥底部的半径
height float 圆锥的高度,也就是圆锥从底面中心到顶点的垂直距离
direction np.ndarray 圆锥的朝向
show_base bool 是否显示圆锥的底面
v_range list[float] 圆锥纵向扫描的范围,用于控制圆锥侧面的纵向形状
u_min float 圆锥横向扫描的最小角度,影响圆锥侧面的横向分布
checkerboard_colors bool 是否使用棋盘格颜色模式,有助于在视觉上区分圆锥的不同部分

这些参数允许用户灵活地创建和自定义圆锥体对象。

例如,

  • 通过调整
    base_radius

    height
    参数,可以改变圆锥的大小和形状;
  • 通过
    direction
    参数,可以控制圆锥的朝向;

  • show_base

    v_range

    u_min

    checkerboard_colors
    参数则提供了对圆锥视觉表现的进一步定制。

2. 主要方法

Cone
类有两个自己特有的改变方向的方法:

名称 说明
get_direction 用于获取圆锥体的方向向量。这个方向向量通常是一个三维向量,表示圆锥体在空间中的朝向。
set_direction 用于设置圆锥体的方向向量。通过传入一个新的三维向量,可以改变圆锥体在空间中的朝向。

3. 使用示例

下面的示例将展示如何创建圆锥体对象、设置其参数、以及调用其方法来改变圆锥体的属性或进行动画。

3.1. 基本的圆锥体

本示例展示了如何在
Manim
场景中创建一个基本的圆锥体。

圆锥体具有指定的底面半径和高度,并且默认方向朝上。

# 创建一个底面半径为1,高度为2的圆锥体,
# 方向朝上(默认方向)
cone = Cone(base_radius=1, height=2)

3.2. 自定义圆锥体

在此示例中,创建了一个圆锥体,并对其进行了自定义设置。

这包括设置圆锥体的
填充颜色

边框颜色

边框宽度
,以及将其移动到场景中的
特定位置

这样,圆锥体不仅具有独特的外观,还位于用户指定的位置。

# 创建一个底面半径为1.5,高度为3的圆锥体,
# 设置填充颜色和边框颜色
cone = Cone(
    base_radius=1.5,
    height=3,
    fill_color=RED,
    stroke_color=YELLOW,
    stroke_width=2,
)
# 将圆锥体移动到场景中的特定位置
cone.move_to(np.array([1, 1, 1]))

3.3. 旋转和缩放

这个示例演示了如何为圆锥体添加动画效果。

首先,创建了一个圆锥体,并随后应用了两种动画:旋转和缩放。

圆锥体先绕y轴旋转了一定角度,然后按比例放大。

# 创建一个底面半径为1,高度为2的圆锥体
cone = Cone(
    base_radius=1,
    height=2,
    fill_color=BLUE,
)
# 将圆锥体添加到场景中
self.play(Create(cone))
# 创建旋转动画,绕y轴旋转90度
self.play(
    Rotate(
        cone,
        axis=Y_AXIS,
        angle=PI / 2,
    ),
)
# 创建缩放动画,将圆锥体放大1.5倍
self.play(cone.animate.scale(1.5))

3.4. 复制和排列

此示例展示了如何复制圆锥体并将其排列成一行。

首先,创建了一个圆锥体作为模板,然后使用循环创建了多个该圆锥体的副本,并将它们稍微移动位置以排列成一行。

# 创建一个底面半径为0.5,高度为1的圆锥体作为模板
cone_tpl = Cone(
    base_radius=0.5,
    height=1,
    fill_color=GREEN,
)
# 创建一个圆锥体数组,
# 每个圆锥体都是模板的复制,并稍微移动位置
cones = VGroup(
    *[cone_tpl.copy().move_to([i, 0, 0]) for i in range(5)]
)
# 将圆锥体数组添加到场景中
self.play(Create(cones))
# 整个圆锥体数组一起移动
self.play(cones.animate.shift(LEFT * 3))

4. 附件

文中的代码只是关键部分的截取,完整的代码共享在网盘中(
cone.py
),

下载地址:
完整代码
(访问密码: 6872)