Misaka-xxw的博客

昨日种种,皆成今我

还是实习期间的笔记,但是没学完。无奖竞猜,我会填坑吗?

简单遗传算法原理

其实这是一张mermaid图,但是我还没加mermaid扩展,脑补吧

1
2
3
4
5
6
7
8
9
10
11
12
13
---
title: 遗传算法的基本框架
config:
flowchart:
curve: monotoneX
---
flowchart LR
开始([开始])-->编码-->产生初始种群-->计算适应度-->chosen{是否满足优化规则}-->|Yes|最佳个体-->encoding["解码(基因到性状)"]-->结束([结束])
chosen-->|No|选择
subgraph 遗传
选择-->交叉-->变异
end
变异--子代-->计算适应度

基本原理博客园

step 1: 编码

编码包括

  1. 二进制编码
  2. 格雷码
  3. 实数编码(高维复杂情况下常用)
  4. 多参数级联编码(多个子串串连)

二进制编码缺点

  1. 进位时要改变所有的位(),用格雷码可以克服
  2. 要先给出求解的精度,没法微调,精度低误差大,精度高串长效率低
  3. 高维优化问题,串非常长,效率很低

格雷码

这里⊕为异或符号

alt text

step 2: 群体设定

可以随机生成,组成初始群体;也可以根据先验知识生成。种群规模一般取为20~100。
规模小了优化性能不好且可能引起未成熟收敛,规模大计算复杂。

step 3: 适应度评估

适应度函数
适应度越高,个体越好。一般最大化问题,适应度函数取;最小化则

设变换前适应度为,变换后适应度为
不同的变换方法:

  • 线性变换:
  • Boltzmann变换:
  • 乘幂变换:
  • 归一化变换:

适应度共享:根据个体在某个距离内与其他个体的临近程度,来确定该个体的适应度应改变多少。
根据距离测度分类:

  • 基因型共享:码空间距离
  • 表现型共享:解空间距离

step 4: 选择

选择:从群体中选择优胜个体,淘汰劣质个体的操作。即从当前群体中选择适应度较高的个体以生成配对池。
选择压力

选择方式如下:

随机选择

  • 选择幅度决定了每个个体被复制的次数
  • 选择幅度由以下两部分组成
    • 确定染色体的期望值
    • 将期望值转换成实际值(即该染色体后代个体)的数目
  • 常用选择方式
    • 轮盘赌(比例选择算子)
    • 一次随机采样

确定性采样

(直接放书本原文了)
确定性采样就是从父代和子代个体中选择最优的个体。具体举例如下:
(1) (μ+λ)-selection(μ个父代,λ个子代,从μ+λ中选择最好的μ个个体);
(2) (μ,λ)-selection(μ个父代,λ个子代,从λ中选择最好的μ个个体);
(3) Truncation selection(截断选择);
(4) Block selection(块选择);
(5) Elitist selection(贪婪选择,在比例选择最优个体时没有被选择,进行强制选择);
(6) The generational replacement(代替换);
(7) Steady-state reproduction(稳态再生,n个最差的父代个体被子代替换)。

混合采样

混合采样同时具有随机性和确定性,具体举例如下:
(1) Tournament selection(竞赛选择);
(2) Binary tournament selection(规模为2的竞赛选择);
(3) Stochastic tournament selection(采用普通的方法计算选择概率,然后采用赌盘选择个体,适应度高的进入群体,否则被抛弃);
(4) Remainder stochastic sampling(随机保留采样,期望选择次数的整数部分确定,小数部分随机)。

step 5: 交叉

交叉:把两个父代个体的部分结构加以替换,生成新的个体,来提高搜索能力。
原理动图

step 6: 变异

变异:将染色体编码串中的某些基因用其它的基因来替换,改善局部搜索能力。

随机变异

1 2 3 4 5 6 7 8 9
1 2 5 3 4 6 7 8 9

或者

1 2 3 4 5 6 7 8 9
1 5 3 4 2 6 7 8 9

实数变异

基于方向的变异

高斯变异

大概是实习阶段学习的,现在补发一下吧8

模拟退火原理

模拟退火原理博客园

温度(T):控制搜索的随机性。
能量(E):评估解的质量。

重点:Metropolis准则

算法随机执行,额外搜索候选解的领域,以一定概率接受比当前解更差的解,避免陷入局部最优。

对于新解和当前解

重点:温度冷却

  1. 指数冷却(几何退火):

    • 公式:
    • 冷却系数,一般
    • 优点:实现简单,参数少,收敛速度可控。
    • 缺点:降温过快可能导致提前收敛,需调参。
  2. 线性冷却:

    • 公式:
    • 优点:直观易理解。
    • 缺点:若 选取不当可能变为负数或降温太慢/太快。
  3. 对数冷却(理论保证):

    • 公式:
    • 优点:在理论上可保证全局收敛(需无限时间)。
    • 缺点:降温极慢,实际问题中不常单独使用。
  4. Cauchy 冷却:

    • 公式:
    • 性能介于指数与对数之间,折中选择。

模拟退火例子

求解有两个自变量函数的最小值

模拟退火算法来求解函数的最小值
函数:

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
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
import os

import matplotlib

matplotlib.use('TkAgg') # 设置后端为 TkAgg
import numpy as np
import matplotlib.pyplot as plt

plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False

"""
目标函数:Rastrigin函数的最小值
"""


def objective_function(x: list):
A = 10
n = len(x)
return A * n + sum([xi ** 2 - A * np.cos(2 * np.pi * xi) for xi in x])


def simulated_annealing(initial_state: list, objective_function,
initial_temperature: float = 100, cool_rate: float = 0.95,
num_iterations: float = 1000, perturbation_scale: float = 0.1):
"""
模拟退火函数
:param initial_state:初始状态
:param objective_function:目标函数
:param initial_temperature:初始温度
:param cool_rate:冷却系数
:param num_iterations:迭代次数
:param perturbation_scale:扰动大小
:return:结果
"""
current_state = initial_state.copy() # 当前状态
best_state = initial_state.copy() # 最优状态
current_energy = objective_function(current_state) # 当前能量
best_energy = current_energy # 最好能量

energies = [current_energy] # 观察所有能量状况
temperatures = [initial_temperature] # 观察温度状况
states = [current_state.copy()] # 观察状态状况
best_states = [] # 最优解
temperature = initial_temperature # 当前温度

for iteration in range(num_iterations): # 迭代
neighbor = current_state + np.random.normal(0, perturbation_scale, len(current_state)) # 正态分布生成一个新的邻居状态
neighbor_energy = objective_function(neighbor) # 邻居状态的能量
delta_energy = neighbor_energy - current_energy # 能量变化量
if delta_energy < 0 or np.random.rand() < np.exp(-delta_energy / temperature): # 更好的状态,或者以一定概率接受比当前解更差的解
current_state = neighbor # 选取邻居为当前状态
current_energy = neighbor_energy

if current_energy < best_energy:
best_state = current_state.copy()
best_energy = current_energy
temperature *= cool_rate # 冷却
energies.append(current_energy)
temperatures.append(temperature)
states.append(current_state.copy())

best_states.append(best_state.copy())
# 每100次迭代记录全局最优解
if (iteration + 1) % 100 == 0:
print(f"Iteration {iteration + 1}/{num_iterations}, "
f"Current Energy: {current_energy:.4f}, "
f"Best Energy: {best_energy:.4f}, "
f"Temperature: {temperature:.4f}")

return {
'best_state': best_state,
'best_energy': best_energy,
'energies': np.array(energies),
'temperatures': np.array(temperatures),
'states': np.array(states),
'best_states_every_10': np.array(best_states) # 新增返回值
}


seed = int.from_bytes(os.urandom(4), byteorder='big', signed=False) # 随机种子
print("种子", seed)
np.random.seed(seed)
initial_state = np.random.uniform(-5.12, 5.12, 2) # 随机取一个点坐标作为起始点
print("initial_state", initial_state)
result = simulated_annealing(
initial_state,
objective_function,
initial_temperature=100,
cool_rate=0.99,
num_iterations=2000,
perturbation_scale=0.5
)

best_state = result['best_state']
print("最终结果", best_state, "误差", np.sqrt(best_state[0] ** 2 + best_state[1] ** 2))

best_states = result['best_states_every_10']
x_opt = best_states[:, 0]
y_opt = best_states[:, 1]

# 绘制函数图像
x = np.linspace(-5.12, 5.12, 200)
y = np.linspace(-5.12, 5.12, 200)
X, Y = np.meshgrid(x, y)
Z = objective_function([X.ravel(), Y.ravel()]).reshape(X.shape)

fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(18, 6))

# 第一个子图:等高线 + 轨迹
contour = ax1.contourf(X, Y, Z, levels=20, cmap='coolwarm')
fig.colorbar(contour, ax=ax1, label='目标函数值')
ax1.set_title(f'Rastrigin函数等高线图,种子={seed}')
ax1.set_xlabel('x1')
ax1.set_ylabel('x2')

# 绘制轨迹与点
ax1.scatter(result['best_state'][0], result['best_state'][1], c='green', s=100,
label='最终最优解', edgecolors='black', zorder=3)
ax1.scatter(initial_state[0], initial_state[1], c='yellow', s=100,
label='初始点', edgecolors='black', zorder=3)
ax1.plot(x_opt, y_opt, 'r-o', linewidth=2, markersize=4, label='优化路径', zorder=2)
for i, (xi, yi) in enumerate(best_states):
ax1.text(xi + 0.5, yi, f'{i + 1}', color='purple', fontsize=10,
ha='center', va='center', bbox=dict(facecolor='white', alpha=0.7), zorder=1)

# 关键:设置纵横比为 1:1,并确保 x/y 范围一致
ax1.set_aspect('equal', adjustable='box')
ax1.set_xlim(-5.12, 5.12)
ax1.set_ylim(-5.12, 5.12)

ax1.legend()
ax1.grid(True)

# 第二个子图:能量曲线(不需要等比例)
ax2.plot(result['energies'], label='能量变化', color='blue', linewidth=1)
ax2.set_title('能量变化')
ax2.set_xlabel('迭代次数')
ax2.set_ylabel('能量值')
ax2.legend()
ax2.grid(True)

plt.tight_layout()
plt.show()

结果如下:
等高线图

这是实习留下的笔记。

GAN 原理

GAN 简介

生成对抗网络(GAN)架构通过对抗性方法评估生成性模型,要同时训练两个模型

  • 生成器G:根据真数据的分布制作假数据
  • 判别器D:评估样本来自训练数据(而不是G的假数据)的概率

作者这样形容:生成器类似于一群造假者,试图制造假币,并在不被发现的情况下使用;而判别器D则类似于警察,试图检测假币。这个游戏竞赛驱使双方改进他们的方法,直到仿冒品与正品无法辨别。

使用反向传播和丢弃算法来训练这两个模型,并且只使用前向传播来训练生成器的样本

论文代码仓库:https://github.com/goodfeli/adversarial

GAN公式

参数有:
噪声变量
生成器G(由参数为 的多层感知器表示的可微函数)
判别器D(由参数为 的多层感知器表示的可微函数)(表示 来自数据而不是 的概率)

极小极大对策,G试图最小化,D试图最大化

一开始,G训练次数不足,总是被D拒绝,所以 改成

优缺点

优点:

  • 计算效率高:不需要马尔可夫链,只需使用反向传播来获得梯度,在学习过程中不需要推理
  • 统计上,无需直接使用数据样本更新,仅通过判别器的梯度进行学习,避免了将输入数据直接复制到生成器参数中(可以融合多种多样的函数)
  • 可以表示非常尖锐,甚至退化的分布,而基于马尔可夫链的方法要求分布有点模糊,以便链能够在模式之间混合(生成结果清晰逼真)

缺点:

  • 没有明确的生成数据分布 表示
  • 在训练期间 必须与 很好地同步。G在不更新D的情况下不能被训练得太多, 将多个不同的输入 映射到相同的输出 ,从而丧失生成数据的多样性(容易出现不稳定、不收敛、模式崩溃等问题)

Pix2pix原理

Pix2pix简介

Pix2pix是一种用于将输入图像“转换”成相应的输出图像的条件对抗网络(cGAN)架构,即“像素到像素”的映射。

相比于卷积神经网络(CNN),Pix2pix而不需要大量专家来手动设计损失函数,而是自动学习适合于实现该目标的损失函数。

论文代码仓库:https://github.com/phillipi/pix2pix
案例演示:https://phillipi.github.io/pix2pix/

代码也可以看后面的CycleGAN代码的Pix2pix部分

Pix2pix公式

条件对抗网络的目标:

为了测试条件化判别器的重要性,作者还比较了无条件变量,其中判别器去掉了原图像

为了防止模糊,作者将L2距离改成了L1距离。

L1范数是向量元素绝对值的和,也称为曼哈顿范数。它在二维空间中的单位球是一个菱形。
L2范数是向量元素平方和的平方根,也称为欧几里得范数。它在二维空间中的单位球是一个圆。

Pix2pix网络架构

生成器和判别器都用了convolution-BatchNorm-ReLu。

生成器:跳跃连接

对于许多图像转换问题,在输入和输出之间共享了大量的低级信息,因此将这可以些信息直接通过网络传输。为了绕过这些信息瓶颈,添加了跳跃连接,遵循“U网”的一般形状。

判别器:马尔可夫判别器(PatchGAN)

L1和L2损失会导致模糊。传统GAN在遇到L1和L2这类损失时,高频清晰度不太清楚。
作者设计了一种判别器体系结构PatchGAN。该判别器尝试对图像中的切成一个一个N×N块,进行真假分类,在图像上卷积地运行这个判别器并平均化,以提供 的最终输出

常规GAN从图像映射到单个标量输出,这表示“真”或“伪”,而 PatchGAN 从 映射到输出 数组

判别器有效地将图像建模为马尔可夫随机场。

马尔可夫随机场(MRF):一种著名的无向图模型,每个结点表示一个或者一组变量,结点之间的边表示两个变量的依赖关系。
马尔可夫随机场有一组势函数,主要用于定义概率分布函数

CycleGAN 原理

CycleGAN 简介

CycleGAN是一种用于图像↔图像转换的生成对抗网络(GAN)架构,特别适用于没有成对训练数据的情况。它通过引入循环一致性损失,实现了从一个域到另一个域的映射,同时保持输入图像的内容不变。

核心在于循环一致性:作者的举例,就像一个句子从英语翻译到法语,再从法语翻译回英语,应该得到原来的英语句子。图像处理同理。(不是简单的向前-向后一致性)

该方法建立在“Pix2pix”框架之上,Pix2pix也是使用了条件生成对抗性网络。但是区别是,Pix2pix测试数据中的输入-输出必须是成对的,CycleGAN不需要是成对的。

关于CycleGAN的用途,作者给了几个案例,包括

  • 艺术画风格转换
  • 物品变形
  • 季节转换
  • 照片与画作转换
  • 照片增强

CycleGAN 公式

目标:学习给定训练样本的两个域X和Y之间的映射函数
两个映射:
两个对抗性判别器:(处理和转换图像),(处理和转换图像
重要术语:对抗性损失、循环一致性损失

例: 是照片, 是画作, 是照片变成画作, 是画作变成照片, 是照片和生成画作的差别, 是真画和生成照片的差别。

对抗性损失

对抗性损失公式:

同理

这个公式和GAN论文里是一样的

循环一致性损失

对抗性损失仅约束了整体分布一致性(因为用了期望值),但无法保证个体输入与期望输出的对应关系。所以引入循环一致性损失。

循环一致性公式:

补充:这里“双竖杠”右下角一个1,是用到了L1范数,用于衡量两个图像之间的差异

整体公式

也就是,目标函数 = X域的对抗性损失 + Y域的对抗性损失 + 重要性常参数 × 循环一致性

随后,作者通过仅保留对抗性损失或者仅保留循环一致性,说明两个公式都缺一不可,而且必须要循环双向。

训练细节

  1. 整体公式中,设置
  2. 使用批大小为1的Adam优化器
  3. 所有网络都是从头开始训练的,学习率为0.0002。在前100个批次保持相同的学习速率0.0002,并在接下来的100个批次中线性衰减到零。
  4. 生成器结构:对于128×128训练图像,使用6个残差块;对于256×256或更高分辨率的训练图像,使用9个残差块。作者详细地分别列出了两种情况的剩余块使用类型。
  5. 判别器结构:70×70PatchGAN

论文其它信息

与其它模型比较

有包括和其它相似目标模型的比较,略

局限性

  1. 几何变换收效甚微,更适合颜色和纹理的变化
  2. 对训练数据分布敏感​,遇到非常规情况可能会混淆
  3. 与有监督方法有性能差距,可能需要某种形式的弱语义监督

代码

参数

基础参数

在文件options/base_options.py里写了一些参数。

参数名 默认值 类型 说明
--dataroot (必填) str 图像路径,包含子目录如 trainA, trainB, valA, valB
--name experiment_name str 实验名称,决定样本与模型保存位置
--checkpoints_dir ./checkpoints str 模型保存根目录
--model cycle_gan str 选择模型类型 `[cycle_gan \ pix2pix \ test \ colorization]`
--input_nc 3 int 输入图像通道数(RGB=3,灰度=1)
--output_nc 3 int 输出图像通道数(RGB=3,灰度=1)
--ngf 64 int 生成器最后一层的滤波器数量
--ndf 64 int 判别器第一层的滤波器数量
--netD basic str 判别器架构 `[basic \ n_layers \ pixel]`(basic 为 70x70 PatchGAN)
--netG resnet_9blocks str 生成器架构 `[resnet_9blocks \ resnet_6blocks \ unet_256 \ unet_128]`
--n_layers_D 3 int 仅当 netD==n_layers 时使用的层数
--norm instance str 归一化方式 `[instance \ batch \ none \ syncbatch]`
--init_type normal str 网络权重初始化方式 `[normal \ xavier \ kaiming \ orthogonal]`
--init_gain 0.02 float 初始化缩放因子
--no_dropout False boolstore_true 若指定则不使用生成器 dropout
--dataset_mode unaligned str 数据集加载方式 `[unaligned不对齐 \ aligned对齐 \ single单个 \ colorization上色]`
--direction AtoB str 转换方向 AtoBBtoA
--serial_batches False boolstore_true 若指定则按顺序取图像,否则随机
--num_threads 4 int 数据加载线程数
--batch_size 1 int 输入批次大小
--load_size 286 int 缩放到该尺寸
--crop_size 256 int 再裁剪到该尺寸
--max_dataset_size inf int(使用 float("inf") 表示不限制) 数据集最大样本数(超过则截断)
--preprocess resize_and_crop str 加载时的缩放与裁剪方式
--no_flip False boolstore_true 若指定则不进行水平翻转数据增强
--display_winsize 256 int 可视化/HTML 显示窗口大小
--epoch latest str 加载哪个 epoch,latest 使用最新缓存模型
--load_iter 0 int 加载指定迭代(>0 时按 iter_[load_iter] 加载)
--verbose False boolstore_true 若指定则打印更多调试信息
--suffix (空字符串) str 自定义后缀,应用于 opt.name(例如 {model}_{netG}_size{load_size}
--use_wandb False boolstore_true 若指定则初始化 wandb 日志
--wandb_project_name CycleGAN-and-pix2pix str wandb 项目名

训练参数

在文件options/train_options.py里写了一些参数。

参数名 默认值 类型 说明
--display_freq 400 int 在屏幕上显示训练结果的频率(步数)
--update_html_freq 1000 int 保存训练结果到 HTML 的频率(步数)
--print_freq 100 int 在控制台打印训练信息的频率(步数)
--no_html False boolstore_true 不将中间训练结果保存到 [opt.checkpoints_dir]/[opt.name]/web/
--save_latest_freq 5000 int 保存最新模型结果的频率(步数)
--save_epoch_freq 5 int 每隔多少个 epoch 保存一次检查点
--save_by_iter False boolstore_true 是否按迭代次数保存模型而非按 epoch
--continue_train False boolstore_true 是否继续训练:加载最新模型并接着训练
--epoch_count 1 int 起始的 epoch 计数(用于保存命名等)
--phase train str 运行阶段,例如 train, val, test
--n_epochs 100 int 使用初始学习率训练的 epoch 数
--n_epochs_decay 100 int 线性衰减学习率到 0 所需的 epoch 数
--beta1 0.5 float Adam 优化器的 momentum 项(beta1)
--lr 0.0002 float Adam 优化器的初始学习率
--gan_mode lsgan str GAN 损失类型,示例:vanilla / lsgan / wgangp
--pool_size 50 int 存储之前生成图像的缓冲区大小(用于判别器)
--lr_policy linear str 学习率策略:linear线性衰减 / step阶梯衰减 / plateau监控指标自适应下降 / cosine余弦退火
--lr_decay_iters 50 int 每隔多少次迭代按因子乘以 gamma(仅在某些策略中使用)

测试参数

在文件options/test_options.py里写了一些参数。

参数名 默认值 类型 说明
--results_dir ./results/ str 保存生成结果的目录
--aspect_ratio 1.0 float 生成结果图片的宽高比
--phase test str 运行阶段,示例:train, val, test
--eval False bool(flag,action='store_true' 在测试时使用 eval 模式(调用 model.eval()
--num_test 50 int 要运行的测试图片数量

论文:PHT-CAD: Efficient CAD Parametric Primitive Analysis with Progressive Hierarchical Tuning

PHT-CAD github仓库

ParaCAD 数据集

仓库里暂时没有开源代码,作者在Issues里表示模型后续会开源。评论区表示数据集解压后有245GB,很大。期待作者开源模型。

PHT-CAD简介

术语简称

  • PHT: 渐进性分层调优 / Progressive Hierarchical Tuning
  • CAD: 计算机辅助设计 / Computer-Aided Design
  • PPA: 参数图元分析 / Parametric Primitive Analysis
  • EHP: 高效混合参数化 / Efficient Hybrid Parametrization
  • VLMs: 视觉语言模型 / Vision-Language Models
  • ViT: 视觉Transformer模型 / Vision Transformer
  • P-MSE: 参数均方误差 / Parametric Mean Squared Error
  • MLP: 多层感知器 / Multi-Layer Perceptron

PPA构成

  1. 几何图元
  2. 注释层

PPA两大挑战

  1. 结构约束推理:工程图通过几何约束(例如平行、相切、重合)固定编码,图元之间有复杂的相互依赖性
  2. 高级语义理解:除了识别几何图元之外,PPA还需要深入理解标注、隐式约束和图元之间的层次关系

PHT-CAD框架

  • 标准VLM架构
    • 构成
      • 视觉编码器
      • VLM 视觉语言模型
    • 目的:多模态理解
  • EHP 高效混合参数化
  • 4个专有回归头预测头
    • 目的
      • 预测相关原子组件
      • 辅助VLM的精确数值预测(减少VLM的局限性)
  • P-MSE 参数均方误差
    • 目的:监督数值预测
  • PHT 渐进性分层调优
    • 3个渐进阶段
      • 图元感知调优
      • 结构感知调优
      • 注释几何对齐

ParaCAD

  • 原始数据来自两个大数据集
  • 用Draw Param方法为两个大数据集的所有样本生成参数化注释
  • 500 0000 图纸
  • 上色+额外注释+3000张真实工业图纸

ParaCAD:有标记的2D PPA

ParaCAD的数据集

该数据集解决两个问题

  1. 缺乏标注层
  2. 更高的结构复杂性和现实世界的约束(例如:完全封闭)

数据集 = 有注释的训练集 + 现实的工业图纸的评估集(更复杂)

训练集有3个子集

  1. 单个图元识别数据
  2. 草图结构感知数据
  3. 标注尺寸的绘图数据

训练集步骤:

  1. 数据预处理和DXF生成,获取圆弧arc、圆circle、直线line和点point(数量<6的太简单了,排除)
  2. 尺寸标注和多格式转换,DXF→JSON
  3. 几何约束提取和JSON结构化
    • JSON = 图元信息(基本几何实体) + 约束信息(图元之间的关系) + 尺寸标注信息(提取的尺寸标签和数值)

ParaCAD的评估指标

传统评估指标 = 图元指标 + 基于视觉的指标

新的度量维度精度(DA, Dimension Accuracy)
三个验证功能:类型正确性、数值一致性和几何元素对齐

公式中 是指示函数,当括号内的式子为真时,指示函数返回1;当括号内的式子为假时,返回0。

  • :图元的基本事实
  • :图元的基本结果
  • 分量 :确保预测的标注类型与地面真实情况匹配,从而标识其是长度、直径、半径还是角度
  • 分量 :检查预测尺寸值是否在内偏离
  • 分量 :确保几何元素在位置公差 内对齐
  • 尺寸精度(DA)衡量正确预测的比率

PHT-CAD 方法论

EHP 高效混合参数化

参数化策略三种类型:

  1. 隐式策略:利用几何图元的标准化和相对表示,通过方向向量、参考点和参数约束来编码其空间属性。
  2. 基于点的策略:采用标准化的绝对表示,通过显式关键点而不是相对约束来编码几何图元。
  3. 过参数化策略:结合了以上两种策略的参数,旨在通过加入相对约束和显式关键点来丰富模型的几何信息。

EHP:基于点的策略和隐式策略相结合,同时消除冗余信息,以提高效率和一致性。

EHP的3个关键修改:

  1. 删除了方向向量,仅通过直线和圆弧的起点和终点坐标表示直线和圆弧。
  2. 使用圆和圆弧的中心坐标、半径和起点/终点角度重新定义圆和圆弧的表示法,不再使用基于离散点的表示法。(PS:和我们的表示法是一致的)
  3. 将坐标归一化到[0,1000]的范围,提供了一个相对坐标系,确保在不同分辨率的图像之间进行一致的空间缩放(有助于模型学习位置关系,并提高其对输入维度变化的稳健性)

所以论文的定义:

其中 是一个表示有效性的二进制数 (e.g. 实线或虚线)

整体架构

PHT-CAD框架集成了

  • 基于VIT的视觉编码器
  • 从Qwen2.5派生的文本编码器

该模型通过提出的渐进性分层调优(PHT)策略进行训练。

损失函数

  • 引入了一种新的参数均方误差损失(P-MSE),提高了原始参数的精度
  • 由四个专用回归头生成的数值预测
  • 现有的视觉语言模型(VLM)通常采用交叉熵(CE)损失进行优化。没有显式地解释预测值和基本真实值之间的数值差异,不适合于2D-PPA任务中的细粒度参数估计
  • 均方误差(MSE)损失对偏差进行二次惩罚,确保即使是几何参数中的微小数值差异也能被有效捕获和优化

P-MSE公式

基本事实
预测的概率分布
基于多层感知器(MLP)回归头,应用于从对应于每个图元的特殊令牌中提取的隐藏表示 (没理解?)
基本事实参数
预测令牌的数量
平衡分类和回归目标的两个超参数

PHT 渐进性参数调优

  1. 图元感知调优:识别和分类单个几何图元,并以结构化、参数格式输出其参数
  2. 结构感知调优:感知工程图纸中的所有图元,并理解它们之间的相互依赖和约束
  3. 注释几何对齐:增强模型处理包括尺寸标注的工程图纸的能力,同时预测图元、约束和尺寸信息

css好玩捏

Re:从零开始的实习之旅

2025 年 6月26日 星期四 实习第一天
2025 年 9月30日 星期二 第一阶段结束

转眼间到了厦门的盛夏,期末考试结束了,我决定专心找实习。我留在了厦门。希望能在实习期间学到更多的知识和技能,为将来的职业发展打下坚实的基础。

实习第一天进入公司,上午主要先办理入职,然后拿起上一位实习生配置好的 c++ 代码就看。


Python,启动!

2025 年 10月15日 星期三

今天装了Pycharm、anaconda、Pytorch,跑机器学习模型案例。Pycharm和anaconda的配置还挺熟悉的,anaconda之前卸了装、装了卸的。Pytorch要装GPU版的,也是装过。机器学习也能跑,怎么调参还是要好好学习原理呢。


ollama 本地部署大模型帮助数据分类

2025 年 10月19日 ☁ 星期日 Ollama替我工作

很好,我装了个ollama,试了一下效果还不错。用的是qwen3:8b-q4_K_M模型,似乎跑的有点慢,就换成了qwen3:4b-q4_K_M。快了一点点。操作挺傻瓜的,下载安装一气呵成,然后下载模型也很快。直接在命令行里输入

1
ollama pull qwen3:4b-q4_K_M

alt text

效果还不错!


2025 年 10月22日 ☁ 星期三 小小实习生,小小汇报

望着奇奇怪怪的计算几何算法,我一筹莫展。考虑的特殊情况和问题太多了!机器学习倒是能跑。导师让我做个小汇报,我自己快把自己说糊涂了。后面我可能要碰一点点前后端,自己跑成功了再留下文档。


C++ 未定义行为打赏

2025 年 10月29日 星期三 野指针领域大神

今天写 c++,主要把之前写的几乎没法维护的代码,简化一下。之前完全按照大牛的论文来复现了,伪代码变成真代码太难了。然而,简化却出现了更多的问题。

比较键导致优先队列出现未定义行为

虽然std::set的比较键comp,当comp(a,b)comp(b,a)都返回false时,ab视为一样的值,不重复插入,但是,std::setcomp(a,b)comp(b,a)不能都返回false,否则会出现诡计多端的未定义行为。这还是我自己的逻辑问题,不会报错,糟糕极了。

无论是 comp(a, b)comp(b, a) 都返回 false,还是都返回 true,都破坏了优先队列依赖的严格弱序关系,会导致未定义行为

比较器必须同时满足:

  • 非自反性comp(a, a) 必须为 false
  • 非对称性:如果 comp(a, b)true,则 comp(b, a) 必须为 false
  • 传递性:如果 comp(a, b)truecomp(b, c)true,则 comp(a, c) 必须为 true
比较场景 合规结果 (满足严格弱序) 违规情况一 违规情况二
comp(a, b) true (表示 a < b) false true
comp(b, a) false false true
对严格弱序的违反 - 违反完备性(无法确定a和b的关系) 违反非对称性(a<b和b<a同时成立)
对优先队列的影响 正常构建堆结构 元素关系不确定,堆结构可能错乱 堆结构的逻辑基础完全崩塌

对于整数等基本类型,最安全的方法是使用标准库提供的比较函数对象(如 std::less)或避免使用可能产生歧义的自定义比较逻辑。对于自定义类型,可以使用 std::tie 来构造可靠的比较器。

我犯的错误是双false,有的时候它正常排序了,有的时候第一个元素的一个重要值给我替换成了一个接近零的浮点数?十分诡异。而且一开始我优先队列里还装指针,雪上加霜了

迭代器加指针是画蛇添足

迭代器已经很智能了,外部再记录不需要再使用“迭代器指针”了。
相当于stl已经帮你封装好了优化的迭代器。迭代器对象生命周期与所属的对象自然绑定,迭代器对象本身也不大。我却没事干搞了个双重指针,一个erase还没有delete,一不小心就把堆给破坏掉了,弊远大于利。

std::vector可能导致原来的内存失效

我将指针指向了std::vector里装的东西,因为push_back扩容时,需要新开辟一块空间再复制进去,原空间废弃无主,所以原来的指针就变成了不美味的野生指针。std::list作为权宜之计,可以规避这个问题,但也要小心指针。

conda命令速查表

功能类别 核心命令 说明/示例
环境管理 conda create -n <env_name> python=<version> 创建指定Python版本的新环境
conda activate <env_name> 激活进入指定环境
conda deactivate 退出当前环境
conda env listconda info --envs 列出所有已创建的环境
conda remove -n <env_name> --all 删除整个环境及其所有包
conda create --name <new_env> --clone <old_env> 克隆一个已存在的环境
包管理 conda install <package_name> 在当前环境安装包
conda install <package_name>=<version> 安装指定版本的包
conda install -n <env_name> <package_name> 在指定环境安装包
conda list 列出当前环境下所有已安装的包
conda update <package_name> 更新特定包
conda update --all 更新当前环境下所有包
conda remove <package_name> 从当前环境卸载包
环境配置共享 conda env export > environment.yml 导出当前环境的精确配置(包括版本号)
conda env create -f environment.yml 根据 YAML 文件创建一模一样的环境
conda env export --from-history > environment.yml 导出环境(仅包含明确安装的包,不包含依赖)
配置与清理 conda config --add channels <channel_url> 添加镜像源以加速下载
conda clean --allconda clean -y --all 清理所有安装包和索引缓存

下载安装anaconda

anaconda的下载安装参考此链接:https://blog.csdn.net/zwLoneranger/article/details/138743242

下载anaconda:https://www.anaconda.com/download/success

和教程一模一样的下载安装

配置镜像源

查看镜像源

1
2
conda config --show-sources
conda config --get channels

选择喜欢的镜像源,选一行填,例如

1
2
3
4
5
6
7
8
9
10
11
conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main/
conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free/
conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/

conda config --add channels https://mirrors.ustc.edu.cn/anaconda/pkgs/main/
conda config --add channels https://mirrors.ustc.edu.cn/anaconda/pkgs/free/
conda config --add channels https://mirrors.ustc.edu.cn/anaconda/cloud/conda-forge/

conda config --add channels https://mirrors.sjtug.sjtu.edu.cn/anaconda/pkgs/main/
conda config --add channels https://mirrors.sjtug.sjtu.edu.cn/anaconda/pkgs/free/
conda config --add channels https://mirrors.sjtug.sjtu.edu.cn/anaconda/cloud/conda-forge/

遥远的笔记
也没有写完
备份一下吧
以后还得复习呢

进程

进程的概念

进程的几个定义

  • 一个正在执行的程序
  • 一个正在计算机上执行的程序实例
  • 能分配给处理器并由处理器执行的实体
  • 由一组执行的指令、一个当前状态和一组相关的系统结构表征的活动单元
  • 系统进行资源分配的基本单位
  • 操作系统的基础
  • 线程的容器

进程的两个基本元素

  • 程序代码(prognam code):可能被执行相同程序的其他进程共享
  • 与代码相关的数据集(set of data):当处理器开始执行一个程序,我们把这个执行实体称为一个进程

进程元素

  • 标识符
  • 状态
  • 优先级
  • 程序计数器
  • 内存指针
  • 上下文数据
  • I/O状态信息
  • 记账信息(审计信息)

以上列表信息存在一个称为进程控制块(process control block,PCB)的数据结构中。

进程与程序

进程与程序之间的关系

  • 进程是运行的程序在操作系统中的抽象。
    • 程序=文件(静态可执行文件)
    • 进程=运行程序=程序+运行状态
  • 一个被多次执行的程序可能对应于多个进程。
  • 进程执行需要比程序更多的资源
    • 存储指令和数据的存储器
    • 执行指令的CPU
    • 以及其他资源

      进程和程序之间的区别

      进程 程序
      主动性 主动 被动
      一组有序的代码 正在执行的程序
      时间 短期,动态,不断变化并很快结束的过程 长期,静态,可长时间保存
      成分 进程=程序+数据+PCB
      一个程序可对应多个进程;通过调用,一个进程可包括多个程序

进程的状态与转换

进程轨迹和分派器(调度器)

轨迹

  • 列出为进程而执行的指令序列,可描述单个进程的行为
  • 给出各个进程轨迹的交替方,就可描述处理器的行为

分派器/调度器

  • 使处理器切换进程,调度程序

两状态进程模型

进程的两种状态

  • 运行态
  • 未运行态

两状态
队列图

五状态进程模型

五状态

  • 就绪态:进程已在内存中并可以执行
  • 阻塞态:进程已在内存中并等待一个事件
  • 阻塞/挂起态:进程已在外存中并等待一个事件
  • 就绪/挂起态:进程已在外存中,但只要载入内存就可执行

被挂起的进程

在我们使用ctrl+z强制进程挂起时,是怎么让操作系统认为该进程需要挂起的? 为什么再次执行该程序会有xxx busy的报错? 进程挂起后恢复,之前的数据如何恢复 进程在挂起状态是否仍在执行,为何进程在挂起状态仍然会占用总线

挂起进程的特征

  • 不能立即被执行
  • 为阻止进程执行,可以通过代理把这个进程置于挂起态
    • 代理可以是进程本身,也可以是父进程或操作系统。
  • 进程可能在或者不在等待事件
  • 除非代理显式地命令系统进行状态转换,否则进程无法从这一状态中转移。

进程挂起的原因

p81

事件 说明
交换
其他OS原因
交互式用户请求
定时
父进程请求
交换
  • 涉及将整个进程的一部分从主内存移动到磁盘
  • 当主内存中没有一个进程处于就绪状态时,操作系统会将其中一个被阻塞的进程交换到磁盘上的挂起队列中

进程描述

进程控制

原语

  • 定义:进程控制用的程序段
  • 特点:执行期间不允许中断,是一个不可分割的

进程创建

父进程与子进程

允许一个进程创建另一个进程
父进程:创建者
子进程:被创建的进程

  • 子进程可以继承父进程所拥有的资源
  • 子进程被撤销时,应将其从父进程那里获得的资源还给父进程
  • 撤销父进程时,同时撤销所有的子进程

导致创建进程的操作

  • 终端用户登录系统
  • 作业调度
  • 系统提供服务
  • 用户程序的应用请求

创建原语

  1. 为新进程分配一个唯一的进程标识号,并申请一个空白的PCB(PCB有限)。若PCB申请失败,则创建失败
  2. 为进程分配其运行所需的资源(内存、文件、I/O设备、CPU时间等),从操作系统或者父进程获得。
    若资源不足,处于创建态,等待内存资源
  3. 初始化PCB
  4. 若进程就绪队列能够接纳新进程,则将新进程插入就绪队列,等待被调度运行

进程终止

  • 一个进程必须有一种方法来表明它的完成
  • 批处理作业中应该包含一个Halt 指令或一个操作系统显式服务调用用于终止
  • 对交互式应用程序,用户的行为将指出何时进程完成,(例如,退出登录, 退出应用程序)
  • 当进程终止的时候,操作系统要回收这个进程占用的内存。

导致终止进程的操作

  • 正常结束
  • 异常结束:存储区越界、保护错、非法指令、特权指令错、运行超时、算数运算错、I/O故障等
  • 外界干预:操作员或操作系统干预、父进程请求和父进程终止

终止原语

  1. 根据被终止进程的标识符,检索出该进程的PCB,从中读出该进程的状态
  2. 若被终止进程处于运行状态,立即终止该进程的执行,将CPU资源分配给其他进程
  3. 若该进程还有子孙进程,则通常需将其所有子孙进程终止
  4. 将该进程所拥有的全部资源,或归还给其父进程,或归还给操作系统
  5. 将该PCB从所在队列(链表)中删除

进程派生

线程

进程与线程

进程与线程的对比

特性 进程 线程
目的 多道程序并发执行,提高资源利用率和系统吞吐量 减小程序在并发执行时所符出的时空开销,提高操作系统的并发性能
单位 拥有资源和独立调度的基本单位 不拥有系统资源访问隶属进程的系统资源,独立调度的基本单位,实际运作单位
开销 分配回收PCB及其他资源,开销大 保存设置少量寄存器的内容,开销小
并发性 不同进程并发、一个进程的多个线程并发 不同进程的线程并发
独立性 地址空间和资源独立而不准其它进程访问(除了共享全局变量) 共享进程的地址空间和资源
多处理器 单线程进程只能运行在1个CPU上 多线程进程可以运行在多个CPU上

并发:互斥和同步

信号量机制

整型信号量

  1. 公有信号量:实现进程的互斥,初始为1或者资源的数目
  2. 私有信号量:实现进程间的同步,初值为0或者某个正整数

信号量S的物理意义

  • S>=0:对应资源的可用数
  • S<0:绝对值表示阻塞队列中等待该资源的进程数

PV操作

P操作:申请一个资源

1
2
3
4
5
Precedure P(Var S:Semphore);
Begin
S:=S-1;
If S<0 then W(S) {执行P操作的进程插入等待队列}
End;

V操作:释放一个资源

1
2
3
4
5
Precedure V(Var S:Semphore);
Begin
S:=S+1;
If S<=0 then R(S) {从阻塞队列中唤醒一个进程}
End;

PV操作实现进程的互斥

1
2
3
P(mutex)
临界区
V(mutex)

PV操作实现进程的同步

协调进程之间相互制约的关系

实战经验

Unix-C

Python

Unity

gitignore 未及时响应

如果想要屏蔽的文件已经被 Git 跟踪(已添加到版本库),.gitignore 就不会再生效。要先将这些文件从 Git 缓存中移除

1
git rm --cached .idea/copilot.data.migration.*.xml

根除commit记录

跳转早期博客

新建一个空分支

1
2
3
git checkout --orphan 新分支
git rm -rf .
git commit --allow-empty -m "初始化新分支"

合并两个不相关的仓库

我将第一学期学习资料的仓库开源了,现在想把春季学期学习资料的仓库合并到秋季学期。它们并不是分支关系,也可以合并

1
2
3
4
5
cd path/to/秋
git remote add second-semester https://github.com/用户名/春.git
git fetch 春
git checkout 新分支
git merge 春/main --allow-unrelated-histories

然后,可能会提示有所冲突,比如说两个仓库的 README.md,可以用 visual studio code 打开冲突文件,删删减减,解决冲突。

1
2
git commit -m "合并第二学期仓库的所有提交历史到空分支"
git push origin 新分支

如果不需要接收春季学期的更新了,可以选择

1
git remote remove 春

jetbrain同时绑定github和gitee

出门在外有时不能使用github,其实jetbrain轻松推送两个仓库。在插件市场中下载git和gitee插件,在gitee网页端的设置中也领个 token
alt text
这样可以愉快地存双倍代码了!

git命令行中文显示八进制编码

1
git config --global core.quotepath false

RAID ( Redundant Array of Independent Disks )即独立磁盘冗余阵列,简称为「磁盘阵列」,其实就是用多个独立的磁盘组成在一起形成一个大的磁盘系统,从而实现比单块磁盘更好的存储性能和更高的可靠性。

RAID级别 别名/原理 最小磁盘数 容量利用率 容错能力 读写性能特点 典型应用场景
RAID 0 条带化 2 100% 无。任一磁盘故障导致全部数据丢失。 读写性能最高。 对性能要求高、数据可丢失的场景,如视频编辑缓存、临时数据处理。
RAID 1 镜像 2 50% 可容忍单盘故障。提供100%数据冗余。 读性能高,写性能较低(需写入两份数据)。 对数据安全性要求极高的场景,如操作系统盘、关键数据库。
RAID 2 海明码校验(已过时) 3(通常需更多) 较低(需多块专用校验盘) 可纠正单比特错误,允许单个磁盘故障。 大数据量连续读写性能尚可;小文件随机读写性能差。校验盘易成瓶颈。 超级计算机、大型机(现已基本被淘汰)。
RAID 3 字节级条带化 + 专用校验盘 3 (N-1)/N 单磁盘容错(允许数据盘或校验盘之一故障)。 持续读取性能好;随机写入性能差(专用校验盘是瓶颈)。 大文件连续读写场景(如视频编辑),现已较少使用。
RAID 5 条带化 + 分布式奇偶校验 3 (N-1)/N 单磁盘容错,允许1块磁盘故障。 读性能高;写性能中等(需计算校验位)。 读多写少的应用,如文件服务器、中小企业存储。
RAID 6 条带化 + 双分布式奇偶校验 4 (N-2)/N 双磁盘容错,允许2块磁盘同时故障。 读性能高;写性能较低(需计算双重校验位)。 对数据安全性要求极高的大容量存储,如医疗影像、档案备份。
RAID 10 镜像 + 条带化 (RAID 1+0) 4 50% 高。允许每组镜像中的一块磁盘故障。 读写性能均很高。 对性能和冗余都有高要求的场景,如高并发数据库、虚拟化平台。

因为很喜欢我的伪代码所以备份一下

条件

  • 全部都是直线段
  • 没有重合的线段
  • 线段端点不重合
  • 没有三条及以上线段在同一点相交

基本算法思路

  1. 事件点优先队列Q
    将所有线段的端点作为事件点,分为起点和终点,按x坐标排序,若x坐标相同则按y坐标排序。使用一个红黑树存储点指针。
  2. 扫描线状态活动集S
    使用一个平衡二叉搜索树来维护当前扫描线与线段,按线段在扫描线上的y坐标排序。每当扫描线经过一个事件点时,更新树的状态。

    由于std::set底层不支持动态更新key值,所以自己实现了平衡搜索树。

  3. 处理事件点
    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
    //处理事件点伪代码 
    while(Q非空)
    {
    弹出事件点p;
    switch(p的类型)
    {
    case 起点:
    p的关联线段l插入到S;
    if(l的上邻居与l相交)
    计算交点并插入Q;
    if(l的下邻居与l相交)
    计算交点并插入Q;
    break;
    case 终点:
    if(l的上邻居与l的下邻居相交)
    计算交点并插入Q;
    l从S中删除;
    break;
    case 交点:
    交点关联的两条线段为l1和l2;
    交换l1和l2在S中的位置;//设定交换后l1在上,l2在下
    if(l1的上邻居与l1相交)
    计算交点并插入Q;
    if(l2的下邻居与l2相交)
    计算交点并插入Q;
    break;
    }
    }
  4. 拆分线段
    记录每个线段对应的事件点指针。如果数量大于2,将线段删除并拆分成多个线段,重新插入到原链表中。

剩下的是付费内容()

0%