PointNet数据预处理+网络训练

PointNet数据预处理+网络训练

    • 数据预处理
    • 分类网络的训练
    • 分割网络训练
    • 分类和分割的结果

数据预处理

数据预处理,这里仅介绍一个shapenetdataset;

class ShapeNetDataset(data.Dataset):def __init__(self,root,npoints=2500,classification=False,class_choice=None,split='train',data_augmentation=True):self.npoints = npoints  # 单个数据集的点数self.root = rootself.catfile = os.path.join(self.root,'synsetoffset2category.txt') #各个类别的数据对应的文件夹的路径self.cat = {}self.data_augmentation = data_augmentation # 是否进行数据增强self.classification = classification       # 数据的种类self.seg_classes = {}with open(self.catfile,'r') as f:for line in f:ls = line.strip().split()self.cat[ls[0]] = ls[1]if not class_choice is None:self.cat = {k: v for k,v in self.cat.items() if k in class_choice}self.id2cat = {v:k for k,v in self.cat.items()}self.meta = {}# 读取已经分类好的数据的地址splitfile = os.path.join(self.root,'train_test_split','shuffled_{}_file_list.json'.format(split))filelist = json.load(open(splitfile,'r'))for item in self.cat:self.meta[item] = []# 数据存储地址的转换for file in filelist:_,category,uuid = file.split('/')if category in self.cat.values():self.meta[self.id2cat[category]].append((os.path.join(self.root,category,'points',uuid+'.pts'),os.path.join(self.root, category, 'points_label', uuid+'.seg')))#按类别存储数据路径self.datapath = []for item in self.cat:for fn in self.meta[item]:self.datapath.append((item,fn[0],fn[1]))self.classes = dict(zip(sorted(self.cat),range(len(self.cat))))print(self.classes)with open(os.path.join(os.path.dirname(os.path.realpath(__file__)),'../misc/num_seg_classes.txt'),'r') as f:for line in f:ls = line.strip().split()self.seg_classes[ls[0]] = int(ls[1])self.num_seg_classes = self.seg_classes[list(self.cat.keys())[0]]print(self.seg_classes,self.num_seg_classes)#__getitem__ 方法通常用于定制类的实例对象的索引访问行为,使得类的实例可以像序列(如列表、元组)或映射(如字典)一样进行索引操作。# 在你的代码片段中,这个方法的定义可能是为了支持类实例的索引访问,比如 instance[index] 的操作。def __getitem__(self, index):fn = self.datapath[index]cls = self.classes[self.datapath[index][0]]point_set = np.loadtxt(fn[1]).astype(np.float32)seg = np.loadtxt((fn[2])).astype(np.int64)choice = np.random.choice(len(seg),self.npoints,replace=True)#resamplepoint_set = point_set[choice,:]#去中心化point_set = point_set - np.expand_dims(np.mean(point_set,axis=0),0)#单位化dist = np.max(np.sqrt(np.sum(point_set ** 2,axis = 1)),0)point_set = point_set / dist# 采用随机旋转和随机高斯抖动对数据进行数据增强if self.data_augmentation:theta = np.random.uniform(0,np.pi*2)rotation_matrix = np.array([[np.cos(theta),-np.sin(theta)],[np.sin(theta),np.cos(theta)]])point_set[:,[0,2]] = point_set[:,[0,2]].dot(rotation_matrix) # 随机旋转point_set += np.random.normal(0,0.02,size=point_set.shape) # 生成的随机数服从均值为 0,标准差为 0.02 的正态分布seg = seg[choice]point_set = torch.from_numpy(point_set)seg = torch.from_numpy(seg)cls = torch.from_numpy(np.array([cls]).astype(np.int64))if self.classification:return point_set,clselse:return  point_set,segdef __len__(self):return len(self.datapath)

分类网络的训练

from __future__ import print_function
import argparse
import os
import random
import torch
import torch.nn.parallel
import torch.optim as optim
import torch.utils.data
import matplotlib.pyplot as pltfrom pointnet.my_dataset import ShapeNetDataset
from pointnet.my_model import PointNetCls,feature_transform_regularizer
import torch.nn.functional as F
from tqdm import tqdm# 初始化记录变量
train_losses = []
test_losses = []
train_accuracies = []
test_accuracies = []
learning_rates = []# 创建绘图函数
def plot_metrics(train_losses, test_losses, train_accuracies, test_accuracies, learning_rates):epochs = range(len(train_losses))plt.figure(figsize=(12, 6))# 绘制损失函数曲线plt.subplot(2, 2, 1)plt.plot(epochs, train_losses, label='Training Loss')plt.plot(epochs, test_losses, label='Test Loss')plt.xlabel('Epoch')plt.ylabel('Loss')plt.legend()plt.title('Loss Curve')# 绘制准确率曲线plt.subplot(2, 2, 2)plt.plot(epochs, train_accuracies, label='Training Accuracy')plt.plot(epochs, test_accuracies, label='Test Accuracy')plt.xlabel('Epoch')plt.ylabel('Accuracy')plt.legend()plt.title('Accuracy Curve')# 绘制学习率曲线plt.subplot(2, 2, 3)plt.plot(epochs, learning_rates, label='Learning Rate')plt.xlabel('Epoch')plt.ylabel('Learning Rate')plt.legend()plt.title('Learning Rate Curve')plt.tight_layout()# 保存图表plt.savefig("result.png")plt.close()# 设置输入参数
parser = argparse.ArgumentParser()
parser.add_argument('--batchSize', type=int, default=32, help='input batch size')
parser.add_argument('--num_points', type=int, default=2500, help='input batch size')
parser.add_argument('--workers', type=int, help='number of data loading workers', default=4)
parser.add_argument('--nepoch', type=int, default=250, help='number of epochs to train for')
parser.add_argument('--outf', type=str, default='cls', help='output folder')
parser.add_argument('--model', type=str, default='', help='model path')
parser.add_argument('--dataset', type=str, required=True, help="dataset path")
parser.add_argument('--dataset_type', type=str, default='shapenet', help="dataset type shapenet|modelnet40")
parser.add_argument('--feature_transform', action='store_true', help="use feature transform")opt = parser.parse_args()
print(opt)blue = lambda x: '\033[94m' + x + '\033[0m'opt.manualSeed = random.randint(1,10000)
print("Random Seed: ",opt.manualSeed)
random.seed(opt.manualSeed)
torch.manual_seed(opt.manualSeed)# 设置数据类型,目前只写ShapeNet这一个数据集
if opt.dataset_type == 'shapenet':dataset = ShapeNetDataset(root = opt.dataset,classification=True,npoints=opt.num_points)test_dataset = ShapeNetDataset(root = opt.dataset,classification=True,split='test',npoints=opt.num_points,data_augmentation=False)
else:exit("wrong dataset type!!!")dataloader = torch.utils.data.DataLoader(dataset,batch_size=opt.batchSize,
shuffle=True,num_workers=int(opt.workers))testdataloader = torch.utils.data.DataLoader(test_dataset,batch_size=opt.batchSize,shuffle=True,num_workers=int(opt.workers))print(len(dataset), len(test_dataset))
num_classes = len(dataset.classes)
print('classes', num_classes)try:os.makedirs(opt.outf)
except OSError:pass# 加载模型
classifier = PointNetCls(k=num_classes,feature_transform=opt.feature_transform)#可以加载预训练模型
if opt.model != '':classifier.load_state_dict(torch.load(opt.model))
#设置损失函数
optimizer = optim.Adam(classifier.parameters(),lr=0.001,betas=(0.9,0.999))
#设置激活函数
scheduler = optim.lr_scheduler.StepLR(optimizer,step_size=20,gamma=0.5)
classifier.cuda()
num_batch = len(dataset) / opt.batchSize
#按照轮次进行训练
for epoch in range(opt.nepoch):scheduler.step()epoch_train_loss = 0epoch_train_correct = 0num_batch = len(dataloader)for i,data in enumerate(dataloader,0):# 获取输入数据和标签points,target = datatarget = target[:,0]# 数据预处理points = points.transpose(2,1)points,target = points.cuda(),target.cuda()optimizer.zero_grad() # 梯度清零classifier = classifier.train() # 设置分类器为训练模式pred ,trans,trans_feat = classifier(points) # 前向传播 pred:[batchsize,classify]# 计算损失函数loss = F.nll_loss(pred,target.squeeze())if opt.feature_transform:loss += feature_transform_regularizer(trans_feat) * 0.001# 反向传播和参数更新loss.backward()optimizer.step()# 记录损失和准确率epoch_train_loss += loss.item()pred_choice = pred.data.max(1)[1]correct = pred_choice.eq(target.data).cpu().sum()epoch_train_correct += correct.item()# 打印训练信息print('[%d: %d/%d] train loss: %f accuracy: %f' % (epoch, i, num_batch, loss.item(), correct.item() / float(opt.batchSize)))# 每隔一定步数进行测试if i % 10 == 0:j, data = next(enumerate(testdataloader, 0))points, target = datatarget = target[:, 0]points = points.transpose(2, 1)points, target = points.cuda(), target.cuda()classifier = classifier.eval()pred, _, _ = classifier(points)loss = F.nll_loss(pred, target)pred_choice = pred.data.max(1)[1]correct = pred_choice.eq(target.data).cpu().sum()print('[%d: %d/%d] %s loss: %f accuracy: %f' % (epoch, i, num_batch, blue('test'), loss.item(), correct.item()/float(opt.batchSize)))# 记录每个epoch的平均损失和准确率train_losses.append(epoch_train_loss / num_batch)train_accuracies.append(epoch_train_correct / (num_batch * opt.batchSize))learning_rates.append(optimizer.param_groups[0]['lr'])# 计算整个测试集的损失和准确率classifier = classifier.eval()epoch_test_loss = 0epoch_test_correct = 0with torch.no_grad():for data in testdataloader:points, target = datatarget = target[:, 0]points = points.transpose(2, 1)points, target = points.cuda(), target.cuda()pred, _, _ = classifier(points)loss = F.nll_loss(pred, target)epoch_test_loss += loss.item()pred_choice = pred.data.max(1)[1]correct = pred_choice.eq(target.data).cpu().sum()epoch_test_correct += correct.item()test_losses.append(epoch_test_loss / len(testdataloader))test_accuracies.append(epoch_test_correct / (len(testdataloader) * opt.batchSize))torch.save(classifier.state_dict(), '%s/cls_model_%d.pth' % (opt.outf, epoch))# 绘制训练过程中的指标变化曲线
plot_metrics(train_losses, test_losses, train_accuracies, test_accuracies, learning_rates)total_correct = 0
total_testset = 0for i,data in tqdm(enumerate(testdataloader, 0)):points, target = datatarget = target[:, 0]points = points.transpose(2, 1)points, target = points.cuda(), target.cuda()classifier = classifier.eval()pred, _, _ = classifier(points)pred_choice = pred.data.max(1)[1]correct = pred_choice.eq(target.data).cpu().sum()total_correct += correct.item()total_testset += points.size()[0]print("final accuracy {}".format(total_correct / float(total_testset)))

分割网络训练

from __future__ import print_function
import argparse
import os
import random
import torch.optim as optim
import torch.utils.data
import matplotlib.pyplot as plt
from pointnet.my_dataset import ShapeNetDataset
from pointnet.my_model import PointNetDenseCls,feature_transform_regularizer
import torch.nn.functional as F
from tqdm import tqdm
import numpy as npdef plot_metrics(train_losses, test_losses, train_accuracies, test_accuracies, mIOUs):epochs = range(len(train_losses))plt.figure(figsize=(16,12))# 绘制损失函数曲线plt.subplot(2, 2, 1)plt.plot(epochs, train_losses, label='Training Loss')plt.plot(epochs, test_losses, label='Test Loss')plt.xlabel('Epoch')plt.ylabel('Loss')plt.legend()plt.title('Loss Curve')# 绘制准确率曲线plt.subplot(2, 2, 2)plt.plot(epochs, train_accuracies, label='Training Accuracy')plt.plot(epochs, test_accuracies, label='Test Accuracy')plt.xlabel('Epoch')plt.ylabel('Accuracy')plt.legend()plt.title('Accuracy Curve')# 绘制mIOU曲线plt.subplot(2, 2, 3)plt.plot(epochs, mIOUs, label='mIOUs')plt.xlabel('Epoch')plt.ylabel('mIOU')plt.legend()plt.title('mIOUs')# 标注mIOU的最大值和最小值max_mIOU = np.max(mIOUs)plt.annotate(f'Max mIOU: {max_mIOU:.2f}', xy=(np.argmax(mIOUs), max_mIOU), xytext=(10, -20),textcoords='offset points', arrowprops=dict(arrowstyle='->', color='blue'), fontsize=10)plt.tight_layout()# 保存图表plt.savefig("seg_result.png")plt.close()
parser = argparse.ArgumentParser()
parser.add_argument('--batchSize', type=int, default=32, help='input batch size')
parser.add_argument('--workers', type=int, help='number of data loading workers', default=4)
parser.add_argument('--nepoch', type=int, default=25, help='number of epochs to train for')
parser.add_argument('--outf', type=str, default='seg', help='output folder')
parser.add_argument('--model', type=str, default='', help='model path')
parser.add_argument('--dataset', type=str, required=True, help="dataset path")
parser.add_argument('--class_choice', type=str, default='Chair', help="class_choice")
parser.add_argument('--feature_transform', action='store_true', help="use feature transform")opt = parser.parse_args()
print(opt)opt.manualSeed = random.randint(1,10000)
print("Random Seed: ",opt.manualSeed)
random.seed(opt.manualSeed)
torch.manual_seed(opt.manualSeed)dataset = ShapeNetDataset(root=opt.dataset,classification=False,class_choice=[opt.class_choice]
)
dataloader = torch.utils.data.DataLoader(dataset,batch_size=opt.batchSize,shuffle=True,num_workers=int(opt.workers)
)testset = ShapeNetDataset(root=opt.dataset,classification=False,class_choice=[opt.class_choice],split='test',data_augmentation=False
)
testdataloader = torch.utils.data.DataLoader(testset,batch_size=opt.batchSize,shuffle=True,num_workers=int(opt.workers)
)
print(len(dataset), len(testset))
num_classes = dataset.num_seg_classes
print('classes', num_classes)
try:os.makedirs(opt.outf)
except OSError:pass
blue = lambda x: '\033[94m' + x + '\033[0m'
classifier = PointNetDenseCls(k=num_classes,feature_transform=opt.feature_transform)
if opt.model != '':classifier.load_state_dict(torch.load(opt.model))
optimizer = optim.Adam(classifier.parameters(),lr=0.001,betas=(0.9,0.999))
scheduler = optim.lr_scheduler.StepLR(optimizer, step_size=20, gamma=0.5)
classifier.cuda()num_batch = len(dataset) / opt.batchSize# Lists to store data for plotting
train_losses = []
train_accuracies = []
test_losses = []
test_accuracies = []
mious = []for epoch in range(opt.nepoch):scheduler.step()epoch_train_loss = 0epoch_train_correct = 0for i, data in enumerate(dataloader, 0):points, target = datapoints = points.transpose(2, 1)points, target = points.cuda(), target.cuda()optimizer.zero_grad()classifier = classifier.train()pred, trans, trans_feat = classifier(points)pred = pred.view(-1, num_classes)target = target.view(-1, 1)[:, 0] - 1#print(pred.size(), target.size())loss = F.nll_loss(pred, target)if opt.feature_transform:loss += feature_transform_regularizer(trans_feat) * 0.001loss.backward()optimizer.step()pred_choice = pred.data.max(1)[1]correct = pred_choice.eq(target.data).cpu().sum()print('[%d: %d/%d] train loss: %f accuracy: %f' % (epoch, i, num_batch, loss.item(), correct.item()/float(opt.batchSize * 2500)))# Append training loss and accuracy for plottingepoch_train_loss += loss.item()epoch_train_correct += correct.item()# 计算整个测试集的损失和准确率epoch_test_loss = 0epoch_test_correct = 0with torch.no_grad():for data in testdataloader:points,target = datapoints = points.transpose(2, 1)points, target = points.cuda(), target.cuda()classifier = classifier.eval()pred, _, _ = classifier(points)pred = pred.view(-1, num_classes)target = target.view(-1, 1)[:, 0] - 1loss = F.nll_loss(pred, target)pred_choice = pred.data.max(1)[1]correct = pred_choice.eq(target.data).cpu().sum()epoch_test_correct += correct.item()epoch_test_loss += loss.item()test_losses.append(epoch_test_loss / len(testdataloader))test_accuracies.append(epoch_test_correct / (len(testdataloader) * opt.batchSize * 2500))# 记录每个epoch的平均损失和准确率train_losses.append(epoch_train_loss / num_batch)train_accuracies.append(epoch_train_correct / (num_batch * opt.batchSize * 2500))print('[%d] %s loss: %f accuracy: %f' % (epoch, blue('test'), epoch_test_loss / len(testdataloader), epoch_test_correct / (len(testdataloader) * opt.batchSize * 2500)))torch.save(classifier.state_dict(), '%s/seg_model_%s_%d.pth' % (opt.outf, opt.class_choice, epoch))## benchmark mIOUshape_ious = []for i,data in tqdm(enumerate(testdataloader, 0)):points, target = datapoints = points.transpose(2, 1)points, target = points.cuda(), target.cuda()classifier = classifier.eval()pred, _, _ = classifier(points)pred_choice = pred.data.max(2)[1]pred_np = pred_choice.cpu().data.numpy()target_np = target.cpu().data.numpy() - 1for shape_idx in range(target_np.shape[0]):parts = range(num_classes)#np.unique(target_np[shape_idx])part_ious = []for part in parts:I = np.sum(np.logical_and(pred_np[shape_idx] == part, target_np[shape_idx] == part))U = np.sum(np.logical_or(pred_np[shape_idx] == part, target_np[shape_idx] == part))if U == 0:iou = 1 #If the union of groundtruth and prediction points is empty, then count part IoU as 1else:iou = I / float(U)part_ious.append(iou)shape_ious.append(np.mean(part_ious))mious.append(np.mean(shape_ious))print("mIOU for class {}: {}".format(opt.class_choice, np.mean(shape_ious)))# Save the data to a txt file
with open('plot_data.txt', 'w') as f:f.write('Train Losses:\n')f.write(','.join(map(str, train_losses)) + '\n')f.write('Train Accuracies:\n')f.write(','.join(map(str, train_accuracies)) + '\n')f.write('Test Losses:\n')f.write(','.join(map(str, test_losses)) + '\n')f.write('Test Accuracies:\n')f.write(','.join(map(str, test_accuracies)) + '\n')f.write('mIOUs:\n')f.write(','.join(map(str, mious)) + '\n')
f.close()
plot_metrics(train_losses,test_losses,train_accuracies,test_accuracies,mious)

分类和分割的结果

shapenet分割结果
在这里插入图片描述shapenet分类结果
在这里插入图片描述

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

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

相关文章

前端应熟知的各种宽度高度

目录 一、window对象- 浏览器对象模型 二、Document对象-文档对象模型 前端做项目时经常需要使用到各种宽度高度,可以从两个地方获得这些数据。 一、window对象- 浏览器对象模型 浏览器对象模型 (BOM) 使 JavaScript 有能力与浏览器"对话"。 所有浏览…

动态图形设计:创造视觉运动的艺术

什么是动态设计?动态设计是一个设计领域,指在用户界面中使用动态效果的设计。简单地说是为了移动用户界面上的元素而设计的。良好的动态设计可以吸引用户的注意,提高用户体验和满意度。动态设计也是界面设计与动态设计的结合,将设…

无人机螺旋桨理论教学培训课程

本文档为一份详细的关于TYTO机器人公司提供的电机和螺旋桨理论及其实验操作的指南。指南首先概述了材料、实验目标以及实验的介绍部分,随后详细阐述了理论问题、实验步骤和附录内容。实验目的在于通过实际测试来测量和理解不同螺旋桨参数对无人机性能的影响&#xf…

上海亚商投顾:沪指5连阴 工业母机概念逆势走强

上海亚商投顾前言:无惧大盘涨跌,解密龙虎榜资金,跟踪一线游资和机构资金动向,识别短期热点和强势个股。 一.市场情绪 三大指数今日继续调整,沪指午后一度跌近1%,随后探底回升跌幅收窄,创业板指…

如何确保消息不被重复消费

一、重复消费问题出现的原因 导致重复消费的原因可能出现在生产者,也可能出现在 MQ 或 消费者。这里说的重复消费问题是指同一个数据被执行了两次,不单单指 MQ 中一条消息被消费了两次,也可能是 MQ 中存在两条一模一样的消费。 生产者&…

基于Python的求职招聘管理系统【附源码】

摘 要 随着互联网技术的不断发展,人类的生活已经逐渐离不开网络了,在未来的社会中,人类的生活与工作都离不开数字化、网络化、电子化与虚拟化的数字技术。从互联网的发展历史、当前的应用现状和发展趋势来看,我们完全可以肯定&…

金融企业数据跨境流动的核心需求是什么?如何才能落地?

在金融行业,涉及到的数据跨境流动的场景多种多样,主要涉及到金融机构的跨国经营、全球贸易以及服务贸易等多个方面: 企业跨国经营:当金融机构进行跨国经营时,如银行在海外设立分支机构或进行跨境投资,会涉及…

408数据结构-图的应用1-最小生成树 自学知识点整理

前置知识:图的遍历 图的应用是408初试历年考查的重点。不过一般而言,这部分内容直接以算法设计题形式考查的可能性极小,更多的是结合图的实例来考查算法的具体操作过程,要求掌握的是手推模拟给定图的各个算法执行过程。此外&#…

说点智驾领域的实话!感知|定位|规划控制|就业……

你们有没有一种感觉,近几年自动驾驶技术栈迭代太快,自己稍不留神就与当下主流技术产生脱节了。 其实说实话,并非只有你如此,行业内的工程师都有类似感受。 智能驾驶行业交流群:点击进 分享几个我们最近聊天中的几位朋…

【动态内存】详解

Hi~!这里是奋斗的小羊,很荣幸您能阅读我的文章,诚请评论指点,欢迎欢迎 ~~ 💥💥个人主页:奋斗的小羊 💥💥所属专栏:C语言 🚀本系列文章为个人学习…

【Git】版本控制器的方式:SVN集中式版本控制工具和Git分布式版本控制工具

一、应用场景 二、版本控制器的方式 三、SVN 集中式版本控制工具 四、Git 分布式版本控制工具 五、Git工作流程 一、应用场景 Git 在开发过程中提供了多种应用场景,帮助开发团队高效地管理代码、协同工作,并保证代码质量。以下是一些具体应用场景和相应…

Rocky Linux设置静态IP

[connection] idens160 uuidcd246f67-c929-362a-809d-f1b44ddc5d25 typeethernet autoconnect-priority-999 interface-nameens160 timestamp1719094243[ethernet][ipv4] ## 在IPV4下面修改如下内容 methodmanual address192.…

每日一题——Python实现PAT乙级1030 完美数列(举一反三+思想解读+逐步优化)五千字好文

一个认为一切根源都是“自己不够强”的INTJ 个人主页:用哲学编程-CSDN博客专栏:每日一题——举一反三Python编程学习Python内置函数 Python-3.12.0文档解读 目录 初次尝试 再次尝试 代码结构 时间复杂度分析 空间复杂度分析 总结 我要更强 时…

【OpenCV 图像处理 Python版】图像处理的基本操作

文章目录 1.图像的 IO 操作1.1 图像读取 imread1.2 图像显示1.2.1 opencv 方式1.2.2 matplotlib 方式 1.3 图像保存 imwrite 2.绘制几何图形1. 绘制直线2. 绘制矩形3. 绘制圆形4. 绘制多边形5. 添加文字 3.获取并修改图像中的像素点3.1 获取像素值3.2 修改像素值3.3 获取和修改…

零基础想学编程,选择哪一门语言更好就业?(非常详细)零基础入门到精通,收藏这一篇就够了_c#就业方向

编程语言的用途广泛,它们各自在不同的领域和应用场景中发挥着重要作用。 零基础初学者在选择编程语言时,可以从就业方向入手选择。 【一一帮助安全学习,所有资源获取处一一】 ①网络安全学习路线 ②20份渗透测试电子书 ③安全攻防357页笔记 …

Maven高级理解属性

属性 在这一章节内容中,我们将学习两个内容,分别是 属性版本管理 属性中会继续解决分模块开发项目存在的问题,版本管理主要是认识下当前主流的版本定义方式。 4.1 属性 4.1.1 问题分析 讲解内容之前,我们还是先来分析问题: …

pdf压缩,pdf压缩在线,pdf压缩在线网页版

当我们遇到PDF文件过大,需要压缩其容量大小时,通常是为了更方便地传输、存储或分享这些文件。PDF文件的大小可能因其包含的图像、字体等元素的数量和质量而有所不同。下面,我们将详细介绍压缩PDF容量大小的方法,帮助您轻松实现文件…

Vite打包速度为什么比webpack快,打包的优劣势在哪里?

大家都有被webpack打包速度搞崩溃的时候,修改一处地方,想预览效果,要等上半天。 Vite比Webpack快的原因 ESM(ES Module)原生支持: Vite基于ESM构建,利用浏览器原生支持的ESM模块加载方式&…

基于 JuiceFS 构建高校 AI 存储方案:高并发、系统稳定、运维简单

中山大学的 iSEE 实验室(Intelligence Science and System) Lab)在进行深度学习任务时,需要处理大量小文件读取。在高并发读写场景下,原先使用的 NFS 性能较低,常在高峰期导致数据节点卡死。此外,NFS 系统的…

【PL理论深化】(7) Ocaml 语言:静态类型语言 | 自动类型推断 | 多态类型和多态函数 | let-多态类型系统

💬 写在前面:OCaml 是一种拥有静态类型系统的语言,本章我们就要探讨静态类型系统。 目录 0x00 静态类型系统 0x01 自动类型推断(automatic type inference) 0x02 多态类型和多态函数 0x03 let-多态类型系统&#…