读3dsr代码②训练

train_dada

首先初始化权重

def weights_init(m):classname = m.__class__.__name__if classname.find('Conv') != -1:m.weight.data.normal_(0.0, 0.02)elif classname.find('BatchNorm') != -1:m.weight.data.normal_(1.0, 0.02)m.bias.data.fill_(0)

他的训练数据是imagenet的rgb,然后利用Perlin 噪声来模拟深度图像

generate_perlin_noise

  1. 初始化旋转变换:

    • rot = iaa.Sequential([iaa.Affine(rotate=(-90, 90))]) 创建一个旋转变换,旋转角度在 -90 到 90 度之间。这个变换后续会应用到 Perlin 噪声上,增加噪声图的多样性。
  2. 确定 Perlin 噪声的尺度:

    • perlin_scalexperlin_scaley 分别代表在 x 和 y 方向上的 Perlin 噪声尺度。这些尺度是随机选取的(默认是2的0~6次幂),用于控制噪声的粗糙度或细腻度。
  3. 生成 Perlin 噪声:

    • perlin_noise = rand_perlin_2d_np((resize_shape[0], resize_shape[1]), res=(perlin_scalex, perlin_scaley)) 生成一个二维的 Perlin 噪声图。
      resize_shape=256,256

    rand_perlin_2d_np

    1. 定义 Perlin 噪声的分辨率和形状:

      • delta = (res[0] / shape[0], res[1] / shape[1])
        d = (shape[0] // res[0], shape[1] // res[1])
        
        delta 计算 Perlin 噪声网格的间隔
        d 计算 Perlin 每个细胞内重复的次数,以适应最终生成的噪声图像的形状。
    2. 创建网格和梯度:

      • grid 创建一个在 [0, 1) 区间内均匀分布的二维网格,用于计算 Perlin 噪声。
      • grid = np.mgrid[0:res[0]:delta[0], 0:res[1]:delta[1]].transpose(1, 2, 0) % 1 创建一个二维数组,其范围在 [0, 1) 内。这是通过 np.mgrid 实现的,该函数生成一个密集的多维“网格”,其中每个维度的步长由 delta 确定。
      • delta 的计算基于所需的噪声图像的分辨率和噪声的尺度,确保 grid 覆盖了 [0, 1) 的范围,且分布均匀。
      • 使用 % 1 是为了确保所有值都在 [0, 1) 的范围内,这是因为 Perlin 噪声是在一个规范化的空间内生成的,其中每个点的值应该位于 [0, 1) 的区间内。
      • angles 生成随机角度,用于确定每个网格点的梯度向量。
      • gradients 根据 angles 生成的二维单位向量表示梯度。
      • tt 是在整个噪声图像区域重复 gradients,以确保每个网格细胞内部有相同的梯度向量。
    3. 计算噪声值:

      • dot 函数计算网格点与梯度向量的点积。
      • n00, n10, n01, n11 计算四个角的点积值。
      • t 使用预定义的渐变函数 fade 对网格坐标进行调整,以实现平滑过渡。
      • 最终的噪声值通过插值函数 lerp_np (一个预定义的线性函数)和上述点积值结合,生成整个噪声图。
  4. 应用旋转变换:

    • perlin_noise = rot(image=perlin_noise) 对生成的 Perlin 噪声图应用旋转变换。
  5. 设置阈值并应用:

    • threshold = torch.rand(1).numpy()[0] * beta + beta 计算阈值,用于后续的二值化处理。
    • perlin_thr = np.where(np.abs(perlin_noise) > threshold, np.ones_like(perlin_noise), np.zeros_like(perlin_noise)) 根据阈值二值化处理 Perlin 噪声,生成阈值化后的噪声图 perlin_thr
  6. 生成归一化 Perlin 噪声:

    • norm_perlin = np.where(np.abs(perlin_noise) > threshold, perlin_noise, np.zeros_like(perlin_noise)) 生成归一化的 Perlin 噪声图 norm_perlin,在噪声值低于阈值的地方设为0。

函数最终返回 norm_perlin(归一化 Perlin 噪声)->perlin_norm、perlin_thr(阈值化 Perlin 噪声)、原始的 perlin_noise 和使用的阈值 threshold->p_thr。

随机缩放噪声:
生成一个 [0, 1] 范围内的随机数 beta
image = beta * perlin_noise:使用这个随机数 beta 对 Perlin 噪声进行缩放,模拟不同深度的变化。

随机平移噪声:
生成另一个 [0, 1] 范围内的随机数 beta2。
image = image + (beta2 * (1 - beta)):将缩放后的噪声图进一步平移,以增加深度图的变化性。

裁剪和调整深度图:
image = np.clip(image, 0.0, 1.0) 确保深度值在 [0, 1] 范围内。
image = np.expand_dims(image, 2) 增加一个维度,使图像从二维变为三维。
image = np.transpose(image, (2, 0, 1)) 调整深度图的维度顺序,适配 PyTorch 的要求。

所以这个深度图是通过模拟而非直接从现实世界的深度传感器获取,和rgb也没有半点关系,完全随机出来的

训练过程中,VectorQuantizerEMA的两个实例里多了几步
首先补充说明一下几个类内初始化变量

self.register_buffer('_ema_cluster_size', torch.zeros(num_embeddings))
这种初始化定义方式是为了准备在后续过程中使用指数移动平均(EMA)来更新聚类大小
register_buffer是nn.Module的一个方法
用于注册一个不需要梯度的缓冲区
这是因为_ema_cluster_size不是模型的参数(不需要学习)
但它是模型的一部分
并且在模型的训练过程中会更新
通过注册为缓冲区
确保在模型保存和加载时_ema_cluster_size也会被保存和加载。
和requires_grad=False的区别可能在于register_buffer的变量一定可以被加载或保存self._ema_w = nn.Parameter(torch.Tensor(num_embeddings, self._embedding_dim))
self._ema_w.data.normal_()self._decay = decay
self._epsilon = epsilon
quantized = torch.matmul(encodings, self._embedding.weight).view(input_shape)
从获得quantized 1,48,48,256开始
# Use EMA to update the embedding vectors
if self.training:self._ema_cluster_size = self._ema_cluster_size * self._decay + \(1 - self._decay) * torch.sum(encodings, 0)这步更新是反映每个嵌入向量当前被选中的频率这是通过将当前的_ema_cluster_size乘以衰减因子_decay然后加上新的观察值(由encodings的求和得到)来实现的。# Laplace smoothing of the cluster sizen = torch.sum(self._ema_cluster_size.data)self._ema_cluster_size = ((self._ema_cluster_size + self._epsilon)/ (n + self._num_embeddings * self._epsilon) * n)对_ema_cluster_size应用拉普拉斯平滑(加一个小的常数_epsilon)以避免任何聚类大小变为零这个操作确保了即使某些聚类在当前批次中未被观察到(即其聚类大小为零)它们的大小也会被设置为一个小的非零值这有助于增加数值稳定性。dw = torch.matmul(encodings.t(), flat_input)self._ema_w = nn.Parameter(self._ema_w * self._decay + (1 - self._decay) * dw)self._embedding.weight = nn.Parameter(self._ema_w / self._ema_cluster_size.unsqueeze(1))更新嵌入向量,确保了嵌入向量的更新考虑了不同嵌入被选择的频率# Loss
e_latent_loss = F.mse_loss(quantized.detach(), inputs)

这种基于EMA的更新策略有助于平滑训练过程中嵌入向量的更新,使模型在训练时更稳定,同时减少由于少数嵌入向量频繁更新而导致的过拟合风险。

最后输出就要前仨
输出loss_b, loss_t, recon_out

loss_vq = loss_b + loss_trecon_depth = recon_out[:,:1,:,:]
recon_rgb = recon_out[:,1:,:,:]# Using L1 loss may work better and lead to improved reconstructions
#l2_recon_d_loss = torch.mean((depth_image - recon_depth)**2)
l2_recon_d_loss = torch.mean(torch.abs(depth_image - recon_depth))
#l2_recon_rgb_loss = torch.mean((rgb_image - recon_rgb)**2)
l2_recon_rgb_loss = torch.mean(torch.abs(rgb_image - recon_rgb))
#l2_recon_loss = torch.mean((model_in - recon_out)**2)
l2_recon_loss = torch.mean(torch.abs(model_in - recon_out))
recon_loss = l2_recon_loss + loss_vq
loss = recon_loss

相当于一个mse损失,一个l1范数损失

train_dsr

训练集相比测试集:

  1. 初始化时通过遍历整个训练集,直接定义全局的im_max和im_min,但是其实后面根本没有用上(???这岂不是浪费启动时间)
  2. 没有读取gt(毕竟还是无监督)
  3. plane_mask和生成的perlin噪声相乘得到msk作为getitem的anomaly_mask。
    随机取一个0~1之间的no_anomaly值,如果>0.5,那么anomaly_mask全为0
  4. 最后rgb和深度图分别随机旋转-15, 15之间的角度

训练过程:

  1. 从拼接深度和rgb得到in_image开始,到sub_res_model_hi之前为止,这段过程是不计算梯度的(其实也就是DiscreteLatentModelGroups这部分,但是中间加了些步骤,所以通过model.xxx的形式实现)
  2. ①得到quantized_t之后先经过下面的generate_fake_anomalies_joined函数得到anomaly_embedding_lo,然后分别和quantized_t经过upsample_t得到up_quantized_t和up_quantized_t_real,二者都是16,256,96,96。
    然后这二者再分别依次和enc_b拼接、经过_pre_vq_conv_bot、经过_vq_vae_bot,最终分别得到quantized_b和quantized_b_real,二者都是16,256,96,96。

generate_fake_anomalies_joined
输入zt是16,256,48,48->features,quantized_t是16,256,48,48->embeddings,embedder_lo._embedding.weight是2048, 256->memory_torch_original->memory_torch,anomaly_mask是16,1,384,384->mask,anomaly_strength_lo = (torch.rand(in_image.shape[0]) * 0.90 + 0.10).cuda()(16,)->strength

异常嵌入生成过程详细分析:

  • 初始化随机嵌入矩阵: random_embeddings 初始化为一个16,2304,256的零张量。这个张量用于存储每个位置随机选择的嵌入。

  • features重塑得到inputs 16,48,48,256

  • 遍历每个样本: 对于 embeddings 中的每个样本 k,执行以下操作:

    • 重塑inputs[k]得到flat_input 2304,256
    • 计算距离: 对于给定的样本 k,计算它的每个特征向量 flat_inputmemory_torch 中所有嵌入向量之间的距离distances_b。
    • 确定替换的嵌入数量: 根据 strength[k]->percentage_vectors 这么一个随机的比重确定每个样本要替换的嵌入数量 topk
    • 选择嵌入: 根据距离选择 topk 个最近的嵌入values,并从这些嵌入中随机选择一个来替换原始嵌入,形成新的嵌入 random_embeddings[k]
  • 调整随机嵌入矩阵形状:random_embeddings 重塑并转置,使其形状与输入嵌入 embeddings 一致。16,48,48,256

  • 可选的嵌入打乱: 以随机概率使用 shuffle_patches 函数打乱嵌入块,增加异常嵌入的多样性。

    • 图像分割成块:
      使用 unfold 操作将每个图像分割成大小为 patch_size 的块,不考虑重叠,无填充。结果 u 包含了图像中所有块的展平形式。
    • 打乱块的顺序:
      u 中的每个图像的块进行随机打乱。这里使用了列表推导式和 torch.randperm 来为每个图像生成一个随机索引,以此来打乱块的顺序。
    • 重构图像:
      使用 fold 操作将打乱后的块重新组合成图像。

    返回重新组合后的图像,它的形状与输入相同,但每个图像内部的块已经被随机打乱。

  • 应用异常掩码:

    • 调整掩码大小: 使用 max_pool2dmask 进行下采样,使其空间维度与 embeddings 相匹配,得到anomaly_mask16,1,48,48
    • 合并嵌入: 通过异常掩码将随机异常嵌入和原始嵌入线性组合,生成最终的异常嵌入 anomaly_embedding

函数返回 anomaly_embedding->anomaly_embedding_lo,它在原始特征中引入了模拟的异常,可用于训练模型进行异常检测。16,256,48,48


第二次的输入是zb,quantized_b,embedder_hi._embedding.weight,anomaly_mask,anomaly_strength_hi= (torch.rand(in_image.shape[0]) * 0.90 + 0.10).cuda()

  1. ②然后再把quantized_b和quantized_b_real分别输入给generate_fake_anomalies_joined,分别输出anomaly_embedding和anomaly_embedding_hi_usebot
use_both = torch.randint(0, 2,(in_image.shape[0],1,1,1)).cuda().float()
use_lo = torch.randint(0, 2,(in_image.shape[0],1,1,1)).cuda().float()
use_hi = (1 - use_lo)
anomaly_embedding_hi_usebot = generate_fake_anomalies_joined(zb_real,quantized_b_real,embedder_hi._embedding.weight,anomaly_mask, strength=anomaly_strength_hi)
anomaly_embedding_lo_usebot = quantized_t
anomaly_embedding_hi_usetop = quantized_b_real
anomaly_embedding_lo_usetop = anomaly_embedding_lo
anomaly_embedding_hi_not_both =  use_hi * anomaly_embedding_hi_usebot + use_lo * anomaly_embedding_hi_usetop
anomaly_embedding_lo_not_both =  use_hi * anomaly_embedding_lo_usebot + use_lo * anomaly_embedding_lo_usetop
anomaly_embedding_hi = (anomaly_embedding * use_both + anomaly_embedding_hi_not_both * (1.0 - use_both)).detach().clone()
anomaly_embedding_lo = (anomaly_embedding_lo * use_both + anomaly_embedding_lo_not_both * (1.0 - use_both)).detach().clone()anomaly_embedding_hi_copy = anomaly_embedding_hi.clone()
anomaly_embedding_lo_copy = anomaly_embedding_lo.clone()
  1. 开始计算梯度后,下面的部分也有变动
recon_feat_hi, recon_embeddings_hi, _ = sub_res_model_hi(anomaly_embedding_hi_copy, embedder_hi)
recon_feat_lo, recon_embeddings_lo, _ = sub_res_model_lo(anomaly_embedding_lo_copy, embedder_lo)
这里之前分别输入的是embeddings_hi和embeddings_lo
recon_feat_xx也就是unet部分的输出output# Reconstruct the image from the anomalous features with the general appearance decoder
up_quantized_anomaly_t = model.upsample_t(anomaly_embedding_lo)
quant_join_anomaly = torch.cat((up_quantized_anomaly_t, anomaly_embedding_hi), dim=1)
recon_image_general = model._decoder_b(quant_join_anomaly)
虽然model.upsample_t和model._decoder_b在torch.no_grad()之外,
但是model并没有计入优化器
所以就算计算了梯度也不会更新它的权重# Reconstruct the image from the reconstructed features
# with the object-specific image reconstruction module
up_quantized_recon_t = model.upsample_t(recon_embeddings_lo)
quant_join = torch.cat((up_quantized_recon_t, recon_embeddings_hi), dim=1)
recon_image_recon = model_decode(quant_join)out_mask = decoder_seg(recon_image_recon,recon_image_general)
out_mask_sm = torch.softmax(out_mask, dim=1)# Calculate losses
loss_feat_hi = torch.nn.functional.mse_loss(recon_feat_hi, quantized_b_real.detach())
loss_feat_lo = torch.nn.functional.mse_loss(recon_feat_lo, quantized_t.detach())
loss_l2_recon_img = torch.nn.functional.mse_loss(in_image, recon_image_recon)
total_recon_loss = loss_feat_lo + loss_feat_hi + loss_l2_recon_img*10# Resize the ground truth anomaly map to closely match the augmented features
down_ratio_x_hi = int(anomaly_mask.shape[3] / quantized_b.shape[3])
anomaly_mask_hi = torch.nn.functional.max_pool2d(anomaly_mask,(down_ratio_x_hi, down_ratio_x_hi)).float()
anomaly_mask_hi = torch.nn.functional.interpolate(anomaly_mask_hi, scale_factor=down_ratio_x_hi)
down_ratio_x_lo = int(anomaly_mask.shape[3] / quantized_t.shape[3])
anomaly_mask_lo = torch.nn.functional.max_pool2d(anomaly_mask,(down_ratio_x_lo, down_ratio_x_lo)).float()
anomaly_mask_lo = torch.nn.functional.interpolate(anomaly_mask_lo, scale_factor=down_ratio_x_lo)
anomaly_mask = anomaly_mask_lo * use_both + (anomaly_mask_lo * use_lo + anomaly_mask_hi * use_hi) * (1.0 - use_both)#anomaly_mask = anomaly_mask * anomaly_type_sum
# Calculate the segmentation loss
segment_loss = loss_focal(out_mask_sm, anomaly_mask)
Focal Loss主要用于解决类别不平衡的问题
在像素级的异常检测任务中,
"类别不平衡"问题通常是指异常区域像素与正常区域像素之间的不平衡。
它通过减少对易分类对象的关注(通过降低它们的损失贡献)
来提高模型对困难或少见类别的关注度。
Focal Loss 的公式是-1 * alpha * (1 - pt)^gamma * log(pt)
其中 pt 是模型对正确类别的预测概率
alpha 和 gamma 是调节损失贡献的超参数。l1_mask_loss = torch.mean(torch.abs(out_mask_sm - torch.cat((1.0 - anomaly_mask, anomaly_mask), dim=1)))
如果模型的预测在某些像素点上极度不准确(即预测值与真实值之间的差异很大),
L1 损失不会像平方差损失(L2 损失)那样对这些错误赋予过高的权重,
从而避免让模型过度适应那些极端的误差。segment_loss = segment_loss + l1_mask_loss 
# L1 is different than in the paper but may improve results in some cases

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/774191.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

代码随想录算法训练营DAY7| C++哈希表Part.2|LeetCode:454.四数相加II、383.赎金信、15. 三数之和、18.四数之和

文章目录 454.四数相加II思路C代码 383.赎金信C 代码 15. 三数之和排序哈希法思路C代码 排序双指针法思路去重C代码 18.四数之和前言剪枝C代码 454.四数相加II 力扣题目链接 文章链接:454.四数相加II 视频链接:学透哈希表,map使用有技巧&…

工业物联网关的应用及相关产品-天拓四方

随着科技的飞速发展,智能制造业已成为工业领域的转型方向。在这一转变中,工业物联网关发挥着至关重要的作用。作为连接物理世界与数字世界的桥梁,工业物联网关不仅实现了设备与设备、设备与云平台之间的互联互通,更通过实时数据采…

0.96寸OLED屏调试 ----(四)

所需设备: 1、USB 转 SPI I2C 适配器;内附链接 2、0.96寸OLED显示模块; 备注:专业版、升级版都适用; 继续我们OLED模块的熟悉 : 指令详解 基础指令 1.设置对比度 (81HA[7:0])  …

贵金属投资热:为何投资者纷纷涌入黄金、白银市场

在当今复杂多变的市场中,贵金属投资热度居高不下,尤其以黄金、白银为代表的贵金属。那这背后的原因到底是什么呢,跟随金田金业的脚步,我们来一探究竟。 贵金属投资之所以备受瞩目,首先源于其独特的避险属性。在全球经济…

兼顾陪读|本科学历律师自费赴美国加州大学伯克利分校访学

S律师拟陪同孩子赴海外就读,决定以访问学者身份,申请美国J类签证出国以兼顾陪读。因本科学历,无文章且有地域要求,自己申请无果后做了全权委托。为此我们酌情制定了三条申请策略,最终落实加州大学伯克利分校的访学职位…

AI大模型学习——AI领域技术发展

目录 前言 一、AI大模型学习的理论基础 二、AI大模型的训练与优化 三、AI大模型在特定领域的应用 四、AI大模型学习的伦理与社会影响 五、未来发展趋势与挑战 总结 前言 在当前技术环境下,AI大模型学习不仅要求研究者具备深厚的数学基础和编程能力&#xff…

鸿蒙打包so及引用

一、打包so 1. 环境,DevEco Studio3.1 2. 创建c工程 创建完成后,如下图: 3. 打包so 先配置 然后 二、引用so 1. 新建普工项目 2. 在src同一层级下创建libs文件夹,将so文件拷入 3. 将c工程中的侧接口文件及声明文件复制到项目…

手术室智慧管理平台

手术室智慧管理平台 所属领域 数字化手术室智慧管理平台处于医疗信息化和医疗智能化领域的交叉点上。它不仅涉及医疗信息系统的建设和管理,更是医疗智能化的典型应用之一。在医疗信息化方面,该平台利用先进的信息技术和数据管理手段,实现对手…

协程库-锁类-实现线程互斥同步

mutex.h:信号量,互斥锁,读写锁,范围锁模板,自旋锁,原子锁 锁 **锁不能进行拷贝操作:**锁是用于管理多线程并发访问共享资源的同步原语。这些锁包括互斥锁(mutex)、读写锁…

Allegro许可更新流程

解锁企业软件管理新篇章,Allegro许可更新流程助力高效运营 在数字化经济的时代,软件已经成为企业运营的关键要素。然而,随着技术的不断更新换代,软件的许可证也需要随之更新。如何确保软件许可的及时更新,避免因过期或…

AI智能分析网关V4使用GB28181注册到EasyCVR平台的具体步骤

旭帆科技的智能分析网关V4内含近40种智能分析算法,包括人体、车辆、消防、环境卫生、异常检测等等,在消防安全、生产安全、行为检测等场景应用十分广泛。如常见的智慧工地、智慧校园、智慧景区、智慧城管等等,还支持抓拍、记录、告警、语音对…

小黑的Vue前端之路:js中通过构造函数封装,设置对象getter和setter方法

js中构造函数创建对象 JavaScript本身并不是设计成面向对象的,所以没有class之类的关键字用来定义类,但JavaScript本身相当灵活,可以利用function关键字来定义类并创建对象。 例如js创建person对象 通过new关键字,把函数当成了创建对象的构造函数 function Pers…

某康安全开发工程师一面

一、反射型XSS跟DOM型XSS的最大区别 DOM型xss和别的xss最大的区别就是它不经过服务器,仅仅是通过网页本身的JavaScript进行渲染触发的。 二、Oracle数据库了解多吗 平常用的多的是MySQL数据库,像Oracle数据库也有了解,但是用的不多。 三、…

Fastjson反序列化漏洞原理与漏洞复现(基于vulhub靶场)

🍬 博主介绍👨‍🎓 博主介绍:大家好,我是 hacker-routing ,很高兴认识大家~ ✨主攻领域:【渗透领域】【应急响应】 【Java、PHP】 【VulnHub靶场复现】【面试分析】 🎉点赞➕评论➕收…

【码银送书第十五期】一本书掌握数字化运维方法,构建数字化运维体系

前言 数字化转型已经成为大势所趋,各行各业正朝着数字化方向转型,利用数字化转型方法论和前沿科学技术实现降本、提质、增效,从而提升竞争力。 数字化转型是一项长期工作,包含的要素非常丰富,如数字化转型顶层设计、…

macOS Sonoma 14.4.1 (23E224) 正式版发布,ISO、IPSW、PKG 下载

macOS Sonoma 14.4.1 (23E224) 正式版发布,ISO、IPSW、PKG 下载 2024 年 3 月 26 日凌晨,macOS Sonoma 14.4.1 更新修复了一个可能导致连接到外部显示器的 USB 集线器无法被识别的问题。它还解决了可能导致 Java 应用程序意外退出的问题,并修…

Linux Sftp和Scp

scp 和 sftp 区别 1 scp 能将远程文件复制到另一个远程机,sftp 不能。sftp为 SSH的其中一部分,是一种传输档案至 Blogger 伺服器的安全方式 2.scp 没有删除/创建远程目录功能,sftp 有。scp 在需要进行验证时会要求你输入密码或口令。 3. FT…

栈与队列的实现逻辑以及底层代码

1.前言 栈和队列不是一种语言独有的结构,而是一个由代码语言设计的一种数据结构。是由人设计出的一种具有特定意义的结构。 2.栈 什么是栈,栈以结构体为节点按要求链接的一种后进先出的一种数据结构(last in first out)简称LIF…

Docker部署一个SpringBoot项目(超级详细)

注意:下面的教程主要是针对 Centos7 的,如果使用的其他发行版会有细微的差别,请查看官方文档。 Docker部署一个SpringBoot项目(超级详细) 一、安装Docker1.卸载旧版2.配置Docker的yum库3.安装Docker4.设置开机自启动5.…

通过一篇文章让你了解如何学习C++

如何学习C 前言一、如何学习C二、别人是怎么学C的21天学会C编程能力与编程年龄 三、自己怎么学总结 前言 学习C需要掌握其基础语法、指针和内存管理、STL库使用、面向对象编程等核心概念。可通过阅读权威书籍、在线教程和参考官方文档来系统学习。实践是关键,通过…