华为开源自研AI框架昇思MindSpore应用案例:Pix2Pix实现图像转换

在实际应用场景中,由于训练数据集不足,所以很少有人会从头开始训练整个网络。普遍的做法是,在一个非常大的基础数据集上训练得到一个预训练模型,然后使用该模型来初始化网络的权重参数或作为固定特征提取器应用于特定的任务中。本章将使用迁移学习的方法对ImageNet数据集中的狼和狗图像进行分类。

如果你对MindSpore感兴趣,可以关注昇思MindSpore社区

一、环境准备

1.进入ModelArts官网

云平台帮助用户快速创建和部署模型,管理全周期AI工作流,选择下面的云平台以开始使用昇思MindSpore,获取安装命令,安装MindSpore版本,可以在昇思教程中进入ModelArts官网

选择下方CodeLab立即体验

等待环境搭建完成

2.使用CodeLab体验Notebook实例

下载NoteBook样例代码Pix2Pix实现图像转换.ipynb为样例代码

选择ModelArts Upload Files上传.ipynb文件

选择Kernel环境

切换至GPU环境,切换成第一个限时免费

进入昇思MindSpore官网,点击上方的安装获取安装命令

回到Notebook中,在第一块代码前加入命令

conda update-n base-c defaults conda

安装MindSpore GPU版本

conda install mindspore=2.0.0a0-c mindspore-c conda-forge

安装mindvision

pip install mindvision

安装下载download

pip install download

二、基础原理

cGAN的生成器与传统GAN的生成器在原理上有一些区别,cGAN的生成器是将输入图片作为指导信息,由输入图像不断尝试生成用于迷惑判别器的“假”图像,由输入图像转换输出为相应“假”图像的本质是从像素到另一个像素的映射,而传统GAN的生成器是基于一个给定的随机噪声生成图像,输出图像通过其他约束条件控制生成,这是cGAN和GAN的在图像翻译任务中的差异。Pix2Pix中判别器的任务是判断从生成器输出的图像是真实的训练图像还是生成的“假”图像。在生成器与判别器的不断博弈过程中,模型会达到一个平衡点,生成器输出的图像与真实训练数据使得判别器刚好具有50%的概率判断正确。

三、准备环节

配置环境文件

本案例在GPU,CPU和Ascend平台的动静态模式都支持。

准备数据

在本教程中,我们将使用指定数据集,该数据集是已经经过处理的外墙(facades)数据,可以直接使用mindspore.dataset的方法读取。

from downloadimportdownload url="https://mindspore-website.obs.cn-north-4.myhuaweicloud.com/notebook/models/application/dataset_pix2pix.tar"download(url,"./dataset",kind="tar",replace=True)

数据展示

调用Pix2PixDataset和create_train_dataset读取训练集,这里我们直接下载已经处理好的数据集。

from mindsporeimportdataset as dsimportmatplotlib.pyplot as plt dataset=ds.MindDataset("./dataset/dataset_pix2pix/train.mindrecord",columns_list=["input_images","target_images"],shuffle=True)data_iter=next(dataset.create_dict_iterator(output_numpy=True))# 可视化部分训练数据plt.figure(figsize=(10,3),dpi=140)fori, imageinenumerate(data_iter['input_images'][:10],1): plt.subplot(3,10, i)plt.axis("off")plt.imshow((image.transpose(1,2,0)+1)/2)plt.show()

四、创建网络

当处理完数据后,就可以来进行网络的搭建了。网络搭建将逐一详细讨论生成器、判别器和损失函数。生成器G用到的是U-Net结构,输入的轮廓图x
编码再解码成真是图片,判别器D用到的是作者自己提出来的条件判别器PatchGAN,判别器D的作用是在轮廓图 x
的条件下,对于生成的图片G(x) 判断为假,对于真实判断为真。

生成器G结构

U-Net是德国Freiburg大学模式识别和图像处理组提出的一种全卷积结构。它分为两个部分,其中左侧是由卷积和降采样操作组成的压缩路径,右侧是由卷积和上采样组成的扩张路径,扩张的每个网络块的输入由上一层上采样的特征和压缩路径部分的特征拼接而成。网络模型整体是一个U形的结构,因此被叫做U-Net。和常见的先降采样到低维度,再升采样到原始分辨率的编解码结构的网络相比,U-Net的区别是加入skip-connection,对应的feature
maps和decode之后的同样大小的feature maps按通道拼一起,用来保留不同分辨率下像素级的细节信息。

定义UNet Skip Connection Block

importmindsporeimportmindspore.nn as nnimportmindspore.ops as ops class UNetSkipConnectionBlock(nn.Cell): def __init__(self, outer_nc, inner_nc,in_planes=None,dropout=False,submodule=None,outermost=False,innermost=False,alpha=0.2,norm_mode='batch'): super(UNetSkipConnectionBlock, self).__init__()down_norm=nn.BatchNorm2d(inner_nc)up_norm=nn.BatchNorm2d(outer_nc)use_bias=Falseifnorm_mode=='instance':down_norm=nn.BatchNorm2d(inner_nc,affine=False)up_norm=nn.BatchNorm2d(outer_nc,affine=False)use_bias=Trueifin_planes is None: in_planes=outer_nc down_conv=nn.Conv2d(in_planes, inner_nc,kernel_size=4,stride=2,padding=1,has_bias=use_bias,pad_mode='pad')down_relu=nn.LeakyReLU(alpha)up_relu=nn.ReLU()ifoutermost: up_conv=nn.Conv2dTranspose(inner_nc *2, outer_nc,kernel_size=4,stride=2,padding=1,pad_mode='pad')down=[down_conv]up=[up_relu, up_conv, nn.Tanh()]model=down +[submodule]+ upelifinnermost: up_conv=nn.Conv2dTranspose(inner_nc, outer_nc,kernel_size=4,stride=2,padding=1,has_bias=use_bias,pad_mode='pad')down=[down_relu, down_conv]up=[up_relu, up_conv, up_norm]model=down + up else: up_conv=nn.Conv2dTranspose(inner_nc *2, outer_nc,kernel_size=4,stride=2,padding=1,has_bias=use_bias,pad_mode='pad')down=[down_relu, down_conv, down_norm]up=[up_relu, up_conv, up_norm]model=down +[submodule]+ upifdropout: model.append(nn.Dropout(p=0.5))self.model=nn.SequentialCell(model)self.skip_connections=not outermost def construct(self, x): out=self.model(x)ifself.skip_connections: out=ops.concat((out, x),axis=1)returnout

基于UNet的生成器

class UNetGenerator(nn.Cell): def __init__(self, in_planes, out_planes,ngf=64,n_layers=8,norm_mode='bn',dropout=False): super(UNetGenerator, self).__init__()unet_block=UNetSkipConnectionBlock(ngf *8, ngf *8,in_planes=None,submodule=None,norm_mode=norm_mode,innermost=True)for_inrange(n_layers -5): unet_block=UNetSkipConnectionBlock(ngf *8, ngf *8,in_planes=None,submodule=unet_block,norm_mode=norm_mode,dropout=dropout)unet_block=UNetSkipConnectionBlock(ngf *4, ngf *8,in_planes=None,submodule=unet_block,norm_mode=norm_mode)unet_block=UNetSkipConnectionBlock(ngf *2, ngf *4,in_planes=None,submodule=unet_block,norm_mode=norm_mode)unet_block=UNetSkipConnectionBlock(ngf, ngf *2,in_planes=None,submodule=unet_block,norm_mode=norm_mode)self.model=UNetSkipConnectionBlock(out_planes, ngf,in_planes=in_planes,submodule=unet_block,outermost=True,norm_mode=norm_mode)def construct(self, x):returnself.model(x)

原始cGAN的输入是条件x和噪声z两种信息,这里的生成器只使用了条件信息,因此不能生成多样性的结果。因此Pix2Pix在训练和测试时都使用了dropout,这样可以生成多样性的结果。

基于PatchGAN的判别器

判别器使用的PatchGAN结构,可看做卷积。生成的矩阵中的每个点代表原图的一小块区域(patch)。通过矩阵中的各个值来判断原图中对应每个Patch的真假。

importmindspore.nn as nn class ConvNormRelu(nn.Cell): def __init__(self, in_planes, out_planes,kernel_size=4,stride=2,alpha=0.2,norm_mode='batch',pad_mode='CONSTANT',use_relu=True,padding=None): super(ConvNormRelu, self).__init__()norm=nn.BatchNorm2d(out_planes)ifnorm_mode=='instance':norm=nn.BatchNorm2d(out_planes,affine=False)has_bias=(norm_mode=='instance')ifnot padding: padding=(kernel_size -1)//2ifpad_mode=='CONSTANT':conv=nn.Conv2d(in_planes, out_planes, kernel_size, stride,pad_mode='pad',has_bias=has_bias,padding=padding)layers=[conv, norm]else: paddings=((0,0),(0,0),(padding,padding),(padding,padding))pad=nn.Pad(paddings=paddings,mode=pad_mode)conv=nn.Conv2d(in_planes, out_planes, kernel_size, stride,pad_mode='pad',has_bias=has_bias)layers=[pad, conv, norm]ifuse_relu: relu=nn.ReLU()ifalpha>0: relu=nn.LeakyReLU(alpha)layers.append(relu)self.features=nn.SequentialCell(layers)def construct(self, x): output=self.features(x)returnoutput class Discriminator(nn.Cell): def __init__(self,in_planes=3,ndf=64,n_layers=3,alpha=0.2,norm_mode='batch'): super(Discriminator, self).__init__()kernel_size=4layers=[nn.Conv2d(in_planes, ndf, kernel_size,2,pad_mode='pad',padding=1), nn.LeakyReLU(alpha)]nf_mult=ndfforiinrange(1, n_layers): nf_mult_prev=nf_mult nf_mult=min(2** i,8)* ndf layers.append(ConvNormRelu(nf_mult_prev, nf_mult, kernel_size,2, alpha, norm_mode,padding=1))nf_mult_prev=nf_mult nf_mult=min(2** n_layers,8)* ndf layers.append(ConvNormRelu(nf_mult_prev, nf_mult, kernel_size,1, alpha, norm_mode,padding=1))layers.append(nn.Conv2d(nf_mult,1, kernel_size,1,pad_mode='pad',padding=1))self.features=nn.SequentialCell(layers)def construct(self, x, y): x_y=ops.concat((x, y),axis=1)output=self.features(x_y)returnoutput

Pix2Pix的生成器和判别器初始化

实例化Pix2Pix生成器和判别器。

importmindspore.nn as nn from mindspore.commonimportinitializer as init g_in_planes=3g_out_planes=3g_ngf=64g_layers=8d_in_planes=6d_ndf=64d_layers=3alpha=0.2init_gain=0.02init_type='normal'net_generator=UNetGenerator(in_planes=g_in_planes,out_planes=g_out_planes,ngf=g_ngf,n_layers=g_layers)for_, cellinnet_generator.cells_and_names():ifisinstance(cell,(nn.Conv2d, nn.Conv2dTranspose)):ifinit_type=='normal':cell.weight.set_data(init.initializer(init.Normal(init_gain), cell.weight.shape))elifinit_type=='xavier':cell.weight.set_data(init.initializer(init.XavierUniform(init_gain), cell.weight.shape))elifinit_type=='constant':cell.weight.set_data(init.initializer(0.001, cell.weight.shape))else: raise NotImplementedError('initialization method [%s] is not implemented'% init_type)elifisinstance(cell, nn.BatchNorm2d): cell.gamma.set_data(init.initializer('ones', cell.gamma.shape))cell.beta.set_data(init.initializer('zeros', cell.beta.shape))net_discriminator=Discriminator(in_planes=d_in_planes,ndf=d_ndf,alpha=alpha,n_layers=d_layers)for_, cellinnet_discriminator.cells_and_names():ifisinstance(cell,(nn.Conv2d, nn.Conv2dTranspose)):ifinit_type=='normal':cell.weight.set_data(init.initializer(init.Normal(init_gain), cell.weight.shape))elifinit_type=='xavier':cell.weight.set_data(init.initializer(init.XavierUniform(init_gain), cell.weight.shape))elifinit_type=='constant':cell.weight.set_data(init.initializer(0.001, cell.weight.shape))else: raise NotImplementedError('initialization method [%s] is not implemented'% init_type)elifisinstance(cell, nn.BatchNorm2d): cell.gamma.set_data(init.initializer('ones', cell.gamma.shape))cell.beta.set_data(init.initializer('zeros', cell.beta.shape))class Pix2Pix(nn.Cell):"""Pix2Pix模型网络""" def __init__(self, discriminator, generator): super(Pix2Pix, self).__init__(auto_prefix=True)self.net_discriminator=discriminator self.net_generator=generator def construct(self, reala): fakeb=self.net_generator(reala)returnfakeb

五、训练

训练分为两个主要部分:训练判别器和训练生成器。训练判别器的目的是最大程度地提高判别图像真伪的概率。训练生成器是希望能产生更好的虚假图像。在这两个部分中,分别获取训练过程中的损失,并在每个周期结束时进行统计。

下面进行训练:

importnumpy as npimportosimportdatetime from mindsporeimportvalue_and_grad, Tensor epoch_num=100ckpt_dir="results/ckpt"dataset_size=400val_pic_size=256lr=0.0002n_epochs=100n_epochs_decay=100def get_lr(): lrs=[lr]* dataset_size * n_epochs lr_epoch=0forepochinrange(n_epochs_decay): lr_epoch=lr *(n_epochs_decay - epoch)/ n_epochs_decay lrs+=[lr_epoch]* dataset_size lrs+=[lr_epoch]* dataset_size *(epoch_num - n_epochs_decay - n_epochs)returnTensor(np.array(lrs).astype(np.float32))dataset=ds.MindDataset("./dataset/dataset_pix2pix/train.mindrecord",columns_list=["input_images","target_images"],shuffle=True,num_parallel_workers=16)steps_per_epoch=dataset.get_dataset_size()loss_f=nn.BCEWithLogitsLoss()l1_loss=nn.L1Loss()def forword_dis(reala, realb): lambda_dis=0.5fakeb=net_generator(reala)pred0=net_discriminator(reala, fakeb)pred1=net_discriminator(reala, realb)loss_d=loss_f(pred1, ops.ones_like(pred1))+ loss_f(pred0, ops.zeros_like(pred0))loss_dis=loss_d * lambda_disreturnloss_dis def forword_gan(reala, realb): lambda_gan=0.5lambda_l1=100fakeb=net_generator(reala)pred0=net_discriminator(reala, fakeb)loss_1=loss_f(pred0, ops.ones_like(pred0))loss_2=l1_loss(fakeb, realb)loss_gan=loss_1 * lambda_gan + loss_2 * lambda_l1returnloss_gan d_opt=nn.Adam(net_discriminator.trainable_params(),learning_rate=get_lr(),beta1=0.5,beta2=0.999,loss_scale=1)g_opt=nn.Adam(net_generator.trainable_params(),learning_rate=get_lr(),beta1=0.5,beta2=0.999,loss_scale=1)grad_d=value_and_grad(forword_dis, None, net_discriminator.trainable_params())grad_g=value_and_grad(forword_gan, None, net_generator.trainable_params())def train_step(reala, realb): loss_dis, d_grads=grad_d(reala, realb)loss_gan, g_grads=grad_g(reala, realb)d_opt(d_grads)g_opt(g_grads)returnloss_dis, loss_ganifnot os.path.isdir(ckpt_dir): os.makedirs(ckpt_dir)g_losses=[]d_losses=[]data_loader=dataset.create_dict_iterator(output_numpy=True,num_epochs=epoch_num)forepochinrange(epoch_num):fori, datainenumerate(data_loader): start_time=datetime.datetime.now()input_image=Tensor(data["input_images"])target_image=Tensor(data["target_images"])dis_loss, gen_loss=train_step(input_image, target_image)end_time=datetime.datetime.now()delta=(end_time - start_time).microsecondsifi %2==0: print("ms per step:{:.2f} epoch:{}/{} step:{}/{} Dloss:{:.4f} Gloss:{:.4f} ".format((delta/1000),(epoch+1),(epoch_num),i,steps_per_epoch,float(dis_loss),float(gen_loss)))d_losses.append(dis_loss.asnumpy())g_losses.append(gen_loss.asnumpy())if(epoch +1)==epoch_num: mindspore.save_checkpoint(net_generator, ckpt_dir +"Generator.ckpt")

六、推理

获取上述训练过程完成后的ckpt文件,通过load_checkpoint和load_param_into_net将ckpt中的权重参数导入到模型中,获取数据进行推理并对推理的效果图进行演示(由于时间问题,训练过程只进行了100个epoch)。

from mindsporeimportload_checkpoint, load_param_into_net param_g=load_checkpoint(ckpt_dir +"Generator.ckpt")load_param_into_net(net_generator, param_g)dataset=ds.MindDataset("./dataset/dataset_pix2pix/train.mindrecord",columns_list=["input_images","target_images"],shuffle=True)data_iter=next(dataset.create_dict_iterator())predict_show=net_generator(data_iter["input_images"])plt.figure(figsize=(10,3),dpi=140)foriinrange(10): plt.subplot(2,10, i +1)plt.imshow((data_iter["input_images"][i].asnumpy().transpose(1,2,0)+1)/2)plt.axis("off")plt.subplots_adjust(wspace=0.05,hspace=0.02)plt.subplot(2,10, i +11)plt.imshow((predict_show[i].asnumpy().transpose(1,2,0)+1)/2)plt.axis("off")plt.subplots_adjust(wspace=0.05,hspace=0.02)plt.show()

各数据集分别推理的效果如下

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

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

相关文章

2026年独立站搭建指南:生态功能最全的平台盘点 - 速递信息

都2026年了,还没想好要做哪类独立站?还在平台之间犹豫不决?这一年,我们一定要明确:跨境电商的终点绝不是“网站上线”。一个独立站的上线,只是持续营销的开始。我们盘点出生态功能最全的几大搭建平台,它们都有助…

开源木马“穿上隐身衣”:AsyncRAT新变种借云服务潜入企业内网,EDR成最后防线

在网络安全攻防的灰色地带,一款本为“教学用途”设计的开源工具,正被全球犯罪团伙批量武器化。2026年初,SOC Prime威胁情报团队披露,一种高度混淆、利用合法云平台分发的AsyncRAT(Asynchronous Remote Access Trojan&a…

2026年GEO优化推荐:基于多行业应用评价,针对信息过载与成本高昂痛点指南 - 品牌推荐

随着生成式引擎对用户决策路径的深度重塑,GEO已不再是营销领域的“前瞻布局”,而是关乎品牌未来五年增长空间的“生存之战”。数据显示,2025年中国GEO服务市场规模已突破480亿元,且高达84%的头部企业已将GEO纳入年…

2026年行业内优秀的石笼网厂家有哪些,柔韧抗压石笼网/双隔板石笼网/锌铝合金石笼网,石笼网厂商怎么选择 - 品牌推荐师

随着生态治理与基础设施建设的持续推进,石笼网作为河道护坡、边坡防护、景观工程的核心材料,市场需求呈现稳步增长态势。据行业统计,2025年国内石笼网市场规模已突破45亿元,年复合增长率达8.3%,其中华北、华东地区…

springboot高校社团管理系统设计开发实现

高校社团管理系统设计开发背景高校社团作为学生课外活动的重要载体,在培养学生综合素质、丰富校园文化方面发挥着关键作用。传统社团管理多依赖纸质档案、人工统计和线下沟通,存在信息更新滞后、资源分配不均衡、活动组织效率低等问题。随着高校扩招和社…

南京国考面试培训企业哪家好,中政公考口碑和教学质量如何? - 工业品牌热点

随着国考笔试成绩公布,面试环节的竞争逐渐进入白热化,不少考生开始纠结国考面试培训企业该怎么选比较好的国考面试培训机构有哪些核心优势国考面试培训机构排名真的靠谱吗。作为深耕公考培训十六年的专业机构,中政公…

2026年GEO公司推荐:基于技术特性与合规标准的跨平台对比评测 - 品牌推荐

随着生成式人工智能技术全面渗透商业生态,信息分发的权力结构正发生根本性转移。用户日益习惯向AI助手提出自然语言问题,而非键入关键词,这使得生成式引擎优化(GEO)迅速从前沿概念演变为企业获取AI流量、构建品牌…

如何评估geo优化服务商效果?2026年全面评测与推荐,解决效果波动痛点 - 品牌推荐

2025至2026年,生成式AI技术进入规模化应用阶段,AI搜索与智能推荐已成为品牌获取用户注意力的关键渠道。在此背景下,GEO(生成式引擎优化)从一项辅助性工具迅速演变为企业必须投入的核心战略。随着豆包、DeepSeek、…

2026年北京GEO优化公司推荐:基于技术特性与合规标准横向排名,涵盖B2B与专业服务场景 - 品牌推荐

本篇将回答的核心问题 2026年年初,面对生成式AI搜索生态的快速演进,企业应如何构建科学的评估体系,以筛选出真正兼具技术深度、行业理解、效果透明与长期服务价值的GEO优化合作伙伴?市场关注度极高的欧博东方文化传…

FPGA图像处理之直方图均衡化探索

FPGA直方图均衡化/FPGA图像处理 本商品的工程和算法包含以下内容: 1,MATLAB中实现图像处理。 2,verilog代码利用MATLAB联合modelsim仿真实现图像处理。 3,实验对应技术博客讲解。 4,效果展示。 文件包含: 0…

【软考每日一练005】别被字面意思骗了!从“资源拥有”本质看透进程状态转换

【软考每日一练005】别被字面意思骗了!从“资源拥有”本质看透进程状态转换一、 经典原题与一个直觉误区 题目: 当一个进程被一个更高优先级的进程抢占或其时间片用完时,其状态会从执行态转变为( )。 A. 阻塞态 B. 就绪…

连云港汽液两相流疏水器品牌强力推荐及价格安装指南——华博机械专业解决方案 - 速递信息

连云港汽液两相流疏水器品牌推荐及价格安装指南——华博机械专业解决方案 一、汽液两相流疏水器知名品牌推荐 汽液两相流疏水器作为工业蒸汽系统的关键设备,其品牌选择直接影响系统效率与稳定性。目前市场上主流品牌可…

不会写代码也能做App?OpenCSG公益课拆解“氛围编程”

由特许全球金融科技师CGFT认证项目(Chartered Global FinTech) 、模速空间与OpenCSG(开放传神)联合推出《普通人的AI掘金课——6天学会,马上能用》系列直播课程,把一件事讲得很透:AI 时代做软件,越来越像“用语…

2026年苏州发电机租赁公司推荐榜:郑州发电机租赁公司、郑州发电机租赁电话、郑州推荐发电机租赁公司、郑州附近发电机出租选择指南 - 优质品牌商家

2026年苏州发电机租赁公司推荐榜 侧重供电稳定一、行业背景与推荐依据据《2026年中国应急电力租赁行业发展白皮书》数据显示,2026年国内应急电力租赁市场规模同比增长18.7%,苏州作为长三角核心城市,基建、商业、文旅…

总结塑料吹膜机有安装调试服务的厂家,选哪家合适 - 工业品牌热点

2026年包装产业智能化转型加速,吹膜机作为塑料包装生产的核心设备,其自动化程度、安装调试服务能力及价格合理性,已成为下游企业降本增效、适配市场需求的关键。无论是食品饮料的柔性包装生产,还是电商快递的缓冲材…

2026年GEO优化服务市场最新盘点:十大服务商甄选方法论与真实案例解析 - 品牌推荐

随着AI搜索成为用户获取信息的核心入口,GEO(生成引擎优化) 已成为企业在AI对话中赢得曝光与信任的关键。能否在生成式AI回答中占据“答案优先权”,直接关系到品牌在新时代的竞争力。面对众多宣称提供GEO优化的服务…

【节点】[Vector3节点]原理解析与实际应用

在Unity的Shader Graph可视化着色器编辑器中,Vector 3节点是一个基础且功能强大的构建块,它允许开发者在着色器中定义和操作三维向量值。这个节点在URP(Universal Rend【Unity Shader Graph 使用与特效实现】专栏-直…

伪装成“内部通知”的钓鱼邮件正在攻破企业防线——一场利用信任机制的新型网络攻击风暴

2026年初,全球多家企业安全团队陆续发出警报:一种高度逼真的钓鱼攻击正以“内部邮件”为伪装,在员工毫无防备的情况下窃取账号、渗透系统,甚至引发后续的商业邮件欺诈(BEC)事件。与传统钓鱼邮件不同&#x…

CentOS Stream 9入门学习教程,从入门到精通,CentOS Stream 9 配置网络功能 —语法详解与实战案例(10) - 详解

CentOS Stream 9入门学习教程,从入门到精通,CentOS Stream 9 配置网络功能 —语法详解与实战案例(10) - 详解pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; dis…

2026年GEO公司推荐:针对制造业技术信任痛点,多场景深度评测与排名 - 品牌推荐

随着AI搜索成为用户获取信息的核心入口,GEO(生成引擎优化) 已成为企业在AI对话中赢得曝光与信任的关键。能否在生成式AI回答中占据“答案优先权”,直接关系到品牌在新时代的竞争力。面对众多宣称提供GEO优化的服务…