使用PyTorch AlexNet预训练模型对新数据集进行训练及预测

news2024/11/15 10:15:19

      在 https://blog.csdn.net/fengbingchun/article/details/112709281 中介绍了AlexNet网络,这里使用PyTorch中提供的AlexNet预训练模型对新数据集进行训练,然后使用生成的模型进行预测。主要包括三部分:新数据集自动拆分、训练、预测

      1.新数据集自动拆分:这里使用从网络下载的西瓜、冬瓜图像作为新数据集,只有2类。西瓜存放在单独的目录下,名为watermelon;冬瓜存放在单独的目录下,名为wintermelon。图像总数为264张。

      以下为自动拆分数据集的实现代码:

import cv2
import os
import random
import shutil
import numpy as np

class SplitClassifyDataset:
	"""split the classification dataset"""

	def __init__(self, path_src, path_dst, ratios=(0.8, 0.1, 0.1)):
		"""
		path_src: source dataset path
		path_dst: the path to the split dataset
		ratios: they are the ratios of train set, validation set, and test set, respectively 
		"""
		assert len(ratios) == 3, f"the length of ratios is not 3: {len(ratios)}"
		assert abs(ratios[0] + ratios[1] + ratios[2] - 1) < 1e-05, f"ratios sum must be 1: {ratios[0]}, {ratios[1]}, {ratios[2]}"

		self.path_src = path_src
		self.path_dst = path_dst
		self.ratio_train = ratios[0]
		self.ratio_val = ratios[1]
		self.ratio_test = ratios[2]

		self.is_resize = False
		self.fill_value = None
		self.shape = None

		self.length_total = None
		self.classes = None

		self.mean = None
		self.std = None

		self.supported_img_formats = (".bmp", ".jpeg", ".jpg", ".png", ".webp")

	def resize(self, value=(114,114,114), shape=(256,256)):
		"""
		value: fill value
		shape: the scaled shape
		"""
		self.is_resize = True
		self.fill_value = value
		self.shape = shape

	def _create_dir(self):
		self.classes = [name for name in os.listdir(self.path_src) if os.path.isdir(os.path.join(self.path_src, name))]

		for name in self.classes:
			directory = self.path_dst + "/train/" + name
			if os.path.exists(directory):
				raise ValueError(f"{directory} directory already exists, delete it")
			os.makedirs(directory, exist_ok=True)

			directory = self.path_dst + "/val/" + name
			if os.path.exists(directory):
				raise ValueError(f"{directory} directory already exists, delete it")
			os.makedirs(directory, exist_ok=True)

			if self.ratio_test != 0:
				directory = self.path_dst + "/test/" + name
				if os.path.exists(directory):
					raise ValueError(f"{directory} directory already exists, delete it")
				os.makedirs(directory, exist_ok=True)

	def _get_images(self):
		image_names = {}
		self.length_total = 0

		for class_name in self.classes:
			imgs = []
			for root, dirs, files in os.walk(os.path.join(self.path_src, class_name)):
				for file in files:
					_, extension = os.path.splitext(file)
					if extension in self.supported_img_formats:
						imgs.append(file)
					else:
						print(f"Warning: {self.path_src+'/'+class_name+'/'+file} is an unsupported file")

			image_names[class_name] = imgs
			self.length_total += len(imgs)

		return image_names

	def _get_random_sequence(self, image_names):
		image_sequences = {}

		for name in self.classes:
			length = len(image_names[name])
			numbers = list(range(0, length))
			train_sequence = random.sample(numbers, int(length*self.ratio_train))
			# print("train_sequence:", train_sequence)

			val_sequence = [x for x in numbers if x not in train_sequence]

			if self.ratio_test != 0:
				val_sequence = random.sample(val_sequence, int(length*self.ratio_val))
				# print("val_sequence:", val_sequence)

				test_sequence = [x for x in numbers if x not in train_sequence and x not in val_sequence]
				# print("test_sequence:", test_sequence)
			else:
				test_sequence = []

			image_sequences[name] = [train_sequence, val_sequence, test_sequence]

		return image_sequences

	def _letterbox(self, img):
		shape = img.shape[:2] # current shape: [height, width, channel]
		new_shape = [self.shape[0], self.shape[1]]

		# scale ratio (new / old)
		r = min(new_shape[0] / shape[0], new_shape[1] / shape[1])

		# compute padding
		new_unpad = int(round(shape[1] * r)), int(round(shape[0] * r))
		dw, dh = new_shape[1] - new_unpad[0], new_shape[0] - new_unpad[1] # wh padding
		dw /= 2 # divide padding into 2 sides
		dh /= 2

		if shape[::-1] != new_unpad: # resize
			img = cv2.resize(img, new_unpad, interpolation=cv2.INTER_LINEAR)

		top, bottom = int(round(dh - 0.1)), int(round(dh + 0.1))
		left, right = int(round(dw - 0.1)), int(round(dw + 0.1))
		img = cv2.copyMakeBorder(img, top, bottom, left, right, cv2.BORDER_CONSTANT, value=self.fill_value) # add border

		return img

	def _copy_image(self):
		image_names = self._get_images()
		image_sequences = self._get_random_sequence(image_names) # train, val, test

		sum = 0
		for name in self.classes:
			for i in range(3):
				sum += len(image_sequences[name][i])
		assert self.length_total == sum, f"the length before and afeter the split must be equal: {self.length_total}:{sum}"

		for name in self.classes:
			dirname = ["train", "val", "test"]
			index = [0, 1, 2]
			if self.ratio_test == 0:
				index = [0, 1]

			for idx in index:
				for i in image_sequences[name][idx]:
					image_name = self.path_src + "/" + name + "/" + image_names[name][i]
					dst_dir_name =self.path_dst + "/" + dirname[idx] + "/" + name
					# print(image_name)
					if not self.is_resize: # only copy
						shutil.copy(image_name, dst_dir_name)
					else: # resize, scale the image proportionally
						img = cv2.imread(image_name) # BGR
						if img is None:
							raise FileNotFoundError(f"image not found: {image_name}")

						img = self._letterbox(img)
						cv2.imwrite(dst_dir_name+"/"+image_names[name][i], img)

	def _cal_mean_std(self):
		imgs = []
		std_reds = []
		std_greens = []
		std_blues = []

		for name in self.classes:
			dst_dir_name = self.path_dst + "/train/" + name + "/"

			for root, dirs, files in os.walk(dst_dir_name):
				for file in files:
					# print("file:", dst_dir_name+file)
					img = cv2.imread(dst_dir_name+file)
					if img is None:
						raise FileNotFoundError(f"image not found: {dst_dir_name}{file}")
					imgs.append(img)

					img_array = np.array(img)
					std_reds.append(np.std(img_array[:,:,0]))
					std_greens.append(np.std(img_array[:,:,1]))
					std_blues.append(np.std(img_array[:,:,2]))

		arr = np.array(imgs)
		# print("arr.shape:", arr.shape)
		self.mean = np.mean(arr, axis=(0, 1, 2)) / 255
		self.std = [np.mean(std_reds) / 255, np.mean(std_greens) / 255, np.mean(std_blues) / 255] # B,G,R

	def __call__(self):
		self._create_dir()
		self._copy_image()
		self._cal_mean_std()

	def get_mean_std(self):
		"""get the mean and variance"""
		return self.mean, self.std

if __name__ == "__main__":
	split = SplitClassifyDataset(path_src="../../data/database/classify/melon", path_dst="datasets/melon_new_classify")
	split.resize(shape=(256,256))

	split()
	mean, std = split.get_mean_std()
	print(f"mean: {mean}; std: {std}")
	print("====== execution completed ======")

      说明如下:

      (1).实现类名为SplitClassifyDataset,供外层调用;

      (2).接收的参数包括:源数据集目录(watermelon目录和wintermelon目录所在的目录);结果存放目录;拆分时训练集、验证集、测试集的比例;图像resize后的大小(图像不能变形,默认使用(114,114,114)填充);

      (3).图像会随机被拆分,即每次执行后结果图像会不同;

      (4).拆分后会计算训练集的均值和标准差

      以下为外层代码调用实现:

def split_dataset(src_dataset_path, dst_dataset_path, resize, ratios):
	split = SplitClassifyDataset(path_src=src_dataset_path, path_dst=dst_dataset_path, ratios=ast.literal_eval(ratios))

	# print("resize:", type(ast.literal_eval(resize))) # str to tuple
	split.resize(shape=ast.literal_eval(resize))

	split()
	mean, std = split.get_mean_std()
	print(f"mean: {mean}; std: {std}")

      执行后结果如下图所示:输出均值和标准差(后面训练和预测时都需要);新生成的目录组织结构(每个目录下存放一类)满足PyTorch的要求

      2.训练:

      (1).下载AlexNet预训练模型:仅第一次执行时会从网络下载

def load_pretraind_model():
	model = models.alexnet(weights=models.AlexNet_Weights.IMAGENET1K_V1) # the first execution will download model: alexnet-owt-7be5be79.pth, pos: C:\Users\xxxxxx/.cache\torch\hub\checkpoints\alexnet-owt-7be5be79.pth
	# print("model:", model)

	return model

      (2).加载拆分后的数据集:

def load_dataset(dataset_path, mean, std, labels_file):
	mean = ast.literal_eval(mean) # str to tuple
	std = ast.literal_eval(std)
	# print(f"type: {type(mean)}, {type(std)}")

	train_transform = transforms.Compose([
		transforms.RandomHorizontalFlip(p=0.5),
		transforms.CenterCrop(224),
		transforms.ToTensor(),
		transforms.Normalize(mean=mean, std=std),
	])

	train_dataset = ImageFolder(root=dataset_path+"/train", transform=train_transform)
	print(f"train dataset length: {len(train_dataset)}; classes: {train_dataset.class_to_idx}; number of categories: {len(train_dataset.class_to_idx)}")

	train_loader = DataLoader(train_dataset, batch_size=32, shuffle=True, num_workers=0)

	val_transform = transforms.Compose([
		transforms.CenterCrop(224),
		transforms.ToTensor(),
		transforms.Normalize(mean=mean, std=std),
	])

	val_dataset = ImageFolder(root=dataset_path+"/val", transform=val_transform)
	print(f"val dataset length: {len(val_dataset)}; classes: {val_dataset.class_to_idx}")
	assert len(train_dataset.class_to_idx) == len(val_dataset.class_to_idx), f"the number of categories int the train set must be equal to the number of categories in the validation set: {len(train_dataset.class_to_idx)} : {len(val_dataset.class_to_idx)}"

	val_loader = DataLoader(val_dataset, batch_size=32, shuffle=True, num_workers=0)

	write_labels(train_dataset.class_to_idx, labels_file)

	return len(train_dataset.class_to_idx), len(train_dataset), len(val_dataset), train_loader, val_loader

      (3).将对应的索引和标签写入文件:索引从0开始,依次加1;标签即拆分后的目录名;后面预测时会需要此文件

def write_labels(class_to_idx, labels_file):
	# print("class_to_idx:", class_to_idx)
	with open(labels_file, "w") as file:
		for key, val in class_to_idx.items():
			file.write("%d %s\n" % (int(val), key))

      (4).可视化训练过程中训练集和验证集的Loss和Accuracy:

def draw_graph(train_losses, train_accuracies, val_losses, val_accuracies):
	plt.subplot(1, 2, 1) # loss
	plt.title("Loss curve")
	plt.xlabel("Epoch Number")
	plt.ylabel("Loss")
	plt.plot(train_losses, color="blue")
	plt.plot(val_losses, color="red")
	plt.legend(["Train Loss", "Val Loss"])

	plt.subplot(1, 2, 2) # accuracy
	plt.title("Accuracy curve")
	plt.xlabel("Epoch Number")
	plt.ylabel("Accuracy")
	plt.plot(train_accuracies, color="blue")
	plt.plot(val_accuracies, color="red")
	plt.legend(["Train Accuracy", "Val Accuracy"])

	plt.show()

      某次的执行结果如下图所示:

      (5).主体代码:代码中加入了提前终止训练的判断条件;生成的最终模型名为best.pth

def train(dataset_path, epochs, mean, std, model_name, labels_file):
	classes_num, train_dataset_num, val_dataset_num, train_loader, val_loader = load_dataset(dataset_path, mean, std, labels_file)

	model = load_pretraind_model()

	in_features = model.classifier[6].in_features
	# print(f"in_features: {in_features}")
	model.classifier[6] = nn.Linear(in_features, classes_num) # modify the number of categories

	device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
	model.to(device)

	optimizer = optim.Adam(model.parameters(), lr=0.0002) # set the optimizer
	criterion = nn.CrossEntropyLoss() # set the loss

	train_losses = []
	train_accuracies = []
	val_losses = []
	val_accuracies = []

	highest_accuracy = 0.
	minimum_loss = 100.

	for epoch in range(epochs):
		# reference: https://learnopencv.com/image-classification-using-transfer-learning-in-pytorch/
		epoch_start = time.time()
		# print(colorama.Fore.CYAN + f"epoch: {epoch+1}/{epochs}")

		train_loss = 0.0 # loss
		train_acc = 0.0 # accuracy
		val_loss = 0.0
		val_acc = 0.0

		model.train() # set to training mode
		for i, (inputs, labels) in enumerate(train_loader):
			inputs = inputs.to(device)
			labels = labels.to(device)
			# print("inputs.size(0):", inputs.size(0))

			optimizer.zero_grad() # clean existing gradients
			outputs = model(inputs) # forward pass
			loss = criterion(outputs, labels) # compute loss
			loss.backward() # backpropagate the gradients
			optimizer.step() # update the parameters

			train_loss += loss.item() * inputs.size(0) # compute the total loss
			_, predictions = torch.max(outputs.data, 1) # compute the accuracy
			correct_counts = predictions.eq(labels.data.view_as(predictions))
			acc = torch.mean(correct_counts.type(torch.FloatTensor)) # convert correct_counts to float
			train_acc += acc.item() * inputs.size(0) # compute the total accuracy
			# print(f"train batch number: {i}; train loss: {loss.item():.4f}; accuracy: {acc.item():.4f}")

		model.eval() # set to evaluation mode
		with torch.no_grad():
			for i, (inputs, labels) in enumerate(val_loader):
				inputs = inputs.to(device)
				labels = labels.to(device)

				outputs = model(inputs) # forward pass
				loss = criterion(outputs, labels) # compute loss
				val_loss += loss.item() * inputs.size(0) # compute the total loss
				_, predictions = torch.max(outputs.data, 1) # compute validation accuracy
				correct_counts = predictions.eq(labels.data.view_as(predictions))
				acc = torch.mean(correct_counts.type(torch.FloatTensor)) # convert correct_counts to float
				val_acc += acc.item() * inputs.size(0) # compute the total accuracy
				# print(f"val batch number: {i}; validation loss: {loss.item():.4f}; accuracy: {acc.item():.4f}")

		avg_train_loss = train_loss / train_dataset_num # average training loss
		avg_train_acc = train_acc / train_dataset_num # average training accuracy
		avg_val_loss = val_loss / val_dataset_num # average validation loss
		avg_val_acc = val_acc / val_dataset_num # average validation accuracy
		train_losses.append(avg_train_loss)
		train_accuracies.append(avg_train_acc)
		val_losses.append(avg_val_loss)
		val_accuracies.append(avg_val_acc)
		epoch_end = time.time()
		print(f"epoch:{epoch+1}/{epochs}; train loss:{avg_train_loss:.4f}, accuracy:{avg_train_acc:.4f}; validation loss:{avg_val_loss:.4f}, accuracy:{avg_val_acc:.4f}; time:{epoch_end-epoch_start:.2f}s")

		if highest_accuracy < avg_val_acc and minimum_loss > avg_val_loss:
			torch.save(model.state_dict(), model_name)
			highest_accuracy = avg_val_acc
			minimum_loss = avg_val_loss

		if avg_val_loss < 0.00001 and avg_val_acc > 0.99999:
			print(colorama.Fore.YELLOW + "stop training early")
			torch.save(model.state_dict(), model_name)
			break

	draw_graph(train_losses, train_accuracies, val_losses, val_accuracies)

      运行结果如下图所示:

      3.预测:

      (1).解析上面2.3中生成的文本文件:

def parse_labels_file(labels_file):
	classes = {}

	with open(labels_file, "r") as file:
		for line in file:
			# print(f"line: {line}")
			idx_value = []
			for v in line.split(" "):
				idx_value.append(v.replace("\n", "")) # remove line breaks(\n) at the end of the line
			assert len(idx_value) == 2, f"the length must be 2: {len(idx_value)}"
			classes[int(idx_value[0])] = idx_value[1]

	# print(f"clases: {classes}; length: {len(classes)}")
	return classes

      (2).保存features:供后期使用,长度为9216

def get_images_list(images_path):
	image_names = []

	p = Path(images_path)
	for subpath in p.rglob("*"):
		if subpath.is_file():
			image_names.append(subpath)

	return image_names

def save_features(model, input_batch, image_name):
	features = model.features(input_batch) # shape: torch.Size([1, 256, 6, 6])
	features = model.avgpool(features)
	features = torch.flatten(features, 1) # shape: torch.Size([1, 9216])

	if torch.cuda.is_available():
		features = features.squeeze().detach().cpu().numpy() # shape: (9216,)
	else:
		features = features.queeeze().detach().numpy()
	# print(f"features: {features}; shape: {features.shape}")

	dir_name = "tmp"
	if not os.path.exists(dir_name):
		os.makedirs(dir_name)

	file_name = Path(image_name)
	file_name = file_name.name
	# print(f"file name: {file_name}")
	features.tofile(dir_name+"/"+file_name+".bin")

      (3).主体代码:

def predict(model_name, labels_file, images_path, mean, std):
	classes = parse_labels_file(labels_file)
	assert len(classes) != 0, "the number of categories can't be 0"

	image_names = get_images_list(images_path)
	assert len(image_names) != 0, "no images found"

	mean = ast.literal_eval(mean) # str to tuple
	std = ast.literal_eval(std)

	model = models.alexnet(weights=None)
	in_features = model.classifier[6].in_features
	model.classifier[6] = nn.Linear(in_features, len(classes)) # modify the number of categories
	# print("alexnet model:", model)
	model.load_state_dict(torch.load(model_name))

	device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
	model.to(device)
	print("image name\t\t\t\t\t\tclass\tprobability")

	model.eval()
	with torch.no_grad():
		for image_name in image_names:
			input_image = Image.open(image_name)
			preprocess = transforms.Compose([
				transforms.CenterCrop(224),
				transforms.ToTensor(),
				transforms.Normalize(mean=mean, std=std)
			])

			input_tensor = preprocess(input_image) # (c,h,w)
			input_batch = input_tensor.unsqueeze(0) # create a mini-batch as expected by the model, (1,c,h,w)
			input_batch = input_batch.to(device)

			output = model(input_batch)
			# print(f"output.shape: {output.shape}")
			probabilities = torch.nn.functional.softmax(output[0], dim=0) # the output has unnormalized scores, to get probabilities, you can run a softmax on it
			max_value, max_index = torch.max(probabilities, dim=0)
			print(f"{image_name}\t\t\t\t\t\t{classes[max_index.item()]}\t{max_value.item():.4f}")

			save_features(model, input_batch, image_name)

      执行结果如下图所示:由结果可知,虽然数据集很少,训练次数也很少,但预测时能百分百预测准确

      支持的输入参数及入口函数如下图所示:

def parse_args():
	parser = argparse.ArgumentParser(description="AlexNet image classification")
	parser.add_argument("--task", required=True, type=str, choices=["split", "train", "predict"], help="specify what kind of task")
	parser.add_argument("--src_dataset_path", type=str, help="source dataset path")
	parser.add_argument("--dst_dataset_path", type=str, help="the path of the destination dataset after split")
	parser.add_argument("--resize", default=(256,256), help="the size to which images are resized when split the dataset")
	parser.add_argument("--ratios", default=(0.8,0.1,0.1), help="the ratio of split the data set(train set, validation set, test set), the test set can be 0, but their sum must be 1")
	parser.add_argument("--epochs", type=int, help="number of training")
	parser.add_argument("--mean", type=str, help="the mean of the training set of images")
	parser.add_argument("--std", type=str, help="the standard deviation of the training set of images")
	parser.add_argument("--model_name", type=str, help="the model generated during training or the model loaded during prediction")
	parser.add_argument("--labels_file", type=str, help="one category per line, the format is: index class_name")
	parser.add_argument("--predict_images_path", type=str, help="predict images path")

	args = parser.parse_args()
	return args

if __name__ == "__main__":
	colorama.init(autoreset=True)
	args = parse_args()

	if args.task == "split":
		# python test_alexnet.py --task split --src_dataset_path ../../data/database/classify/melon --dst_dataset_path datasets/melon_new_classify --resize (256,256) --ratios (0.7,0.2,0.1)
		split_dataset(args.src_dataset_path, args.dst_dataset_path, args.resize, args.ratios)
	elif args.task == "train":
		# python test_alexnet.py --task train --dst_dataset_path datasets/melon_new_classify --epochs 100 --mean (0.52817206,0.60931162,0.59818634) --std (0.2533697287956878,0.22790271847362834,0.2380239874816262) --model_name best.pth --labels_file classes.txt
		train(args.dst_dataset_path, args.epochs, args.mean, args.std, args.model_name, args.labels_file)
	else: # predict
		# python test_alexnet.py --task predict --predict_images_path datasets/melon_new_classify/test --mean (0.52817206,0.60931162,0.59818634) --std (0.2533697287956878,0.22790271847362834,0.2380239874816262) --model_name best.pth --labels_file classes.txt
		predict(args.model_name, args.labels_file, args.predict_images_path, args.mean, args.std)

	print(colorama.Fore.GREEN + "====== execution completed ======")

      GitHub:https://github.com/fengbingchun/NN_Test

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

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

相关文章

C++竞赛初阶L1-14-第六单元-数组(31~33课)543: T456473 年龄与疾病

题目内容 某医院进行一项研究,想知道某项疾病是否与年龄有关。因此对以往的诊断记录进行整理,统计 0-18 、 19-35 、 36-60、 61 及以上这四个年龄段的患者人数占总患者人数的比例。 输入格式 输入共 2 行。 第一行包含一个整数 N(0<n≤100),表示总患者人数。 第二…

OpenCV绘图函数(1)绘制带箭头的直线函数arrowedLine()的使用

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 绘制一个从第一个点指向第二个点的箭头线段。 cv::arrowedLine 函数在图像中绘制一个从 pt1 到 pt2 的箭头。另见 line 函数。 函数原型 void c…

Android Auto推出全新Google助手设计

智能手机与汽车的无缝整合已成为现代驾驶的重要组成部分&#xff0c;而 Android Auto 一直在这一领域处于领先地位。谷歌通过不断推出新功能和更新&#xff0c;体现了其致力于提升 Android Auto 体验的决心。最近&#xff0c;Android Auto 引入了 Google助手的全新设计。 当系…

vue3 使用vue-masonry加载更多,重新渲染

在使用 van-list做上拉加载更多&#xff0c;加载下一页的时候&#xff0c;会出现瀑布图重叠&#xff0c;原因是布局没有重新更新&#xff0c;所以需要 调用 vue-masonry更新布局的方法。 看了源码才知道可以这样用&#xff0c;api都没写&#xff0c;隐藏太深了。。。 vue3中通…

随心笔记--测试报告

项目名称 随心笔记 版本号 / 发布类型 正式发布 测试负责人 ** 测试完成日期 2024.8. 联系方式 130576**** 评审人 批准人 评审日期 批准日期 1.项目背景 设计“随心笔记”这个项目的初衷是为了提供一个便捷、个性化的博客管理平台&#xff0c;满足用户记录生…

yarn vite脚手架 react+ts搭建项目

创建react 1、确保你已经安装了Node.js&#xff08;建议使用最新的LTS版本&#xff09;。 2、安装Yarn&#xff08;如果尚未安装&#xff09;: npm install -g yarn 3、创建新项目: yarn create vite 根据提示安装 vite脚手架&#xff0c;如果已安装无提示信息 直接填写项目名…

人脸表情识别数据集分享(内含处理过的AffectNet等数据集)

前言&#xff1a; 人脸表情识别有三大主流数据集rafdb、affectnet、ferplus&#xff0c;之前跑rafdb精度挺正常的&#xff0c;但是另外两个数据集复现不出来精度&#xff0c;经过几个月的踩坑&#xff0c;我发现其实是数据集的原因。 我在官方找的AffectNet数据集有几十个G&…

SQL 高阶 (sql优化)

批量插入&#xff1a; sql 批量插入&#xff1a; INSERT INTO users(name, age) VALUES (Tom, 20), (Jerry, 22), (Bob, 19); -- 该方式 适合 500条以内。多线程插入批量插入&#xff0c;能够大幅提升插入速度。 经过测试&#xff1a;系统性能&#xff0c;8核16G 插入10w条数…

Leetcode 1108. IP地址无效化 C++实现

Leetcode 1108. IP 地址无效化 问题&#xff1a;给你一个有效的 IPv4 地址address&#xff0c;返回这个 IP 地址的无效化版本。 所谓无效化 IP 地址&#xff0c;其实就是用 "[.]" 代替了每个 "."。 方法1&#xff1a;对字符串挨个进行判断&#xff0c;如…

java程序优化

Java程序的性能优化是一个复杂但非常重要的过程&#xff0c;它涉及多个方面。首先&#xff0c;我们需要识别性能瓶颈的具体位置&#xff0c;这通常可以通过性能分析工具&#xff08;如JProfiler, VisualVM等&#xff09;来完成。以下是一些通用的优化策略&#xff1a; 代码层面…

进程的创建与使用(win32-API)

一、前言 进程可以被视作操作系统中运行程序的一个实例&#xff0c;是系统资源分配和调度的基本单位。每一个进程都拥有自己独立的地址空间、一组状态信息&#xff08;如打开的文件、内存映射等&#xff09;&#xff0c;以及一个或多个线程来执行代码。进程之间的隔离性确保了…

飞睿智能家居人体微动感知雷达模组,宠物喂食器、LED灯控连续波雷达感应开关原理

在智能家居的浪潮中&#xff0c;飞睿智能人体微动感知雷达模组犹如一位“隐形守护者”&#xff0c;以其独特的连续波雷达感应开关原理&#xff0c;为我们带来更为智能、便捷的生活体验。今天&#xff0c;就让我们一起走进这一前沿科技&#xff0c;探索它是如何改变我们的生活。…

IOS 13 网络请求和Moya框架

允许HTTP请求 从iOS9开始&#xff0c;推荐使用HTTPS&#xff0c;如果使用的HTTP&#xff0c;默认情况下会出现如下错误&#xff1a; The resource could not be loaded because the App Transport Security policy requires the use of a secure connection. 这是因为iOS9引…

中俄联袂 助力前行 点燃希望——助残义诊在杭州邦尔骨科医院顺利举行

8月26日&#xff0c;由杭州市残疾人联合会主办、临平区残疾人联合会承办&#xff0c;杭州市邦尔骨科医院协办的“中俄联袂 助力前行 点燃希望”助残义诊活动在该院1号楼门诊大厅顺利进行。此次活动得到了俄罗斯伊里扎洛夫中心医院和杭州市邦尔骨科医院专家的大力支持。 本次义诊…

珈创生物业绩下滑:毛利率连年大降,产能利用率滑坡仍募资扩产

《港湾商业观察》廖紫雯 日前&#xff0c;武汉珈创生物技术股份有限公司&#xff08;以下简称&#xff1a;珈创生物&#xff09;于北交所证券交易中心披露招股书&#xff0c;保荐机构为国投证券。 珈创生物往年IPO进程可谓一波三折。2020年12月&#xff0c;公司曾申报沪交所科…

双向NAT应用场景和配置

域内双向NAT&#xff08;端口回流&#xff09; 局域网内有一台或多台服务器可能需要对外映射提供服务&#xff0c;如内网终端也需要访问&#xff0c;这时如终端通过映射后的公网地址访问会出现无法访问的情况&#xff0c;这时就需要域内双向NAT&#xff08;华三很多路由器会有…

window下kafka3启动多个

准备工作 我们先安装好kafka&#xff0c;并保证启动成功&#xff0c;可参考文章Windows下安装Kafka3-CSDN博客 复制kafka安装文件 kafka3已经内置了zookeeper&#xff0c;所以直接复制就行了 修改zookeeper配置文件 这里我们修改zookeeper配置文件&#xff0c;主要是快照地址…

学习分享:手把手教你使用Python开发一个api数据接口

今天想和大家分享一下Python开发语言&#xff0c;为什么选择Python&#xff0c;因为当今生活中人工智能无处不在&#xff0c;而开发人工智能的首选语言必是Python。今天教大家使用Python开发一个api接口。 Python的开发环境我使用的是PyCharm&#xff0c;安装完成之后创建项目…

单域名SSL证书快速获取指南

在当今互联网的社会&#xff0c;网站安全变得非常重要。SSL证书&#xff0c;作为实现网站HTTPS加密通信的重要手段&#xff0c;不仅能够保护用户数据传输的安全&#xff0c;还能提升网站的信任度和搜索引擎排名等。其中单域名SSL证书是可以保护单个域名的网站&#xff0c;这对于…

TensorBoard快速入门

一、简介 TensorBoard是一套 Web 应用程序&#xff0c;用于检查和了解 TensorFlow 运行和图形。简而言之&#xff0c;就是用于机器学习可视化的工具 TensorBoard 设计为完全离线运行&#xff0c;无需任何 Internet 访问权限。例如&#xff0c;这可能位于本地计算机上、公司防…