深度学习从入门到精通——基于unet++算法实现细胞分割

news/2024/9/17 3:37:25/ 标签: 深度学习, 人工智能

模型定义

import torch
from torch import nn__all__ = ['UNet', 'NestedUNet']class VGGBlock(nn.Module):def __init__(self, in_channels, middle_channels, out_channels):super().__init__()self.relu = nn.ReLU(inplace=True)self.conv1 = nn.Conv2d(in_channels, middle_channels, 3, padding=1)self.bn1 = nn.BatchNorm2d(middle_channels)self.conv2 = nn.Conv2d(middle_channels, out_channels, 3, padding=1)self.bn2 = nn.BatchNorm2d(out_channels)def forward(self, x):out = self.conv1(x)out = self.bn1(out)out = self.relu(out)out = self.conv2(out)out = self.bn2(out)out = self.relu(out)return outclass UNet(nn.Module):def __init__(self, num_classes, input_channels=3, **kwargs):super().__init__()nb_filter = [32, 64, 128, 256, 512]self.pool = nn.MaxPool2d(2, 2)self.up = nn.Upsample(scale_factor=2, mode='bilinear', align_corners=True)#scale_factor:放大的倍数  插值self.conv0_0 = VGGBlock(input_channels, nb_filter[0], nb_filter[0])self.conv1_0 = VGGBlock(nb_filter[0], nb_filter[1], nb_filter[1])self.conv2_0 = VGGBlock(nb_filter[1], nb_filter[2], nb_filter[2])self.conv3_0 = VGGBlock(nb_filter[2], nb_filter[3], nb_filter[3])self.conv4_0 = VGGBlock(nb_filter[3], nb_filter[4], nb_filter[4])self.conv3_1 = VGGBlock(nb_filter[3]+nb_filter[4], nb_filter[3], nb_filter[3])self.conv2_2 = VGGBlock(nb_filter[2]+nb_filter[3], nb_filter[2], nb_filter[2])self.conv1_3 = VGGBlock(nb_filter[1]+nb_filter[2], nb_filter[1], nb_filter[1])self.conv0_4 = VGGBlock(nb_filter[0]+nb_filter[1], nb_filter[0], nb_filter[0])self.final = nn.Conv2d(nb_filter[0], num_classes, kernel_size=1)def forward(self, input):x0_0 = self.conv0_0(input)x1_0 = self.conv1_0(self.pool(x0_0))x2_0 = self.conv2_0(self.pool(x1_0))x3_0 = self.conv3_0(self.pool(x2_0))x4_0 = self.conv4_0(self.pool(x3_0))x3_1 = self.conv3_1(torch.cat([x3_0, self.up(x4_0)], 1))x2_2 = self.conv2_2(torch.cat([x2_0, self.up(x3_1)], 1))x1_3 = self.conv1_3(torch.cat([x1_0, self.up(x2_2)], 1))x0_4 = self.conv0_4(torch.cat([x0_0, self.up(x1_3)], 1))output = self.final(x0_4)return outputclass NestedUNet(nn.Module):def __init__(self, num_classes, input_channels=3, deep_supervision=False, **kwargs):super().__init__()nb_filter = [32, 64, 128, 256, 512]self.deep_supervision = deep_supervisionself.pool = nn.MaxPool2d(2, 2)self.up = nn.Upsample(scale_factor=2, mode='bilinear', align_corners=True)self.conv0_0 = VGGBlock(input_channels, nb_filter[0], nb_filter[0])self.conv1_0 = VGGBlock(nb_filter[0], nb_filter[1], nb_filter[1])self.conv2_0 = VGGBlock(nb_filter[1], nb_filter[2], nb_filter[2])self.conv3_0 = VGGBlock(nb_filter[2], nb_filter[3], nb_filter[3])self.conv4_0 = VGGBlock(nb_filter[3], nb_filter[4], nb_filter[4])self.conv0_1 = VGGBlock(nb_filter[0]+nb_filter[1], nb_filter[0], nb_filter[0])self.conv1_1 = VGGBlock(nb_filter[1]+nb_filter[2], nb_filter[1], nb_filter[1])self.conv2_1 = VGGBlock(nb_filter[2]+nb_filter[3], nb_filter[2], nb_filter[2])self.conv3_1 = VGGBlock(nb_filter[3]+nb_filter[4], nb_filter[3], nb_filter[3])self.conv0_2 = VGGBlock(nb_filter[0]*2+nb_filter[1], nb_filter[0], nb_filter[0])self.conv1_2 = VGGBlock(nb_filter[1]*2+nb_filter[2], nb_filter[1], nb_filter[1])self.conv2_2 = VGGBlock(nb_filter[2]*2+nb_filter[3], nb_filter[2], nb_filter[2])self.conv0_3 = VGGBlock(nb_filter[0]*3+nb_filter[1], nb_filter[0], nb_filter[0])self.conv1_3 = VGGBlock(nb_filter[1]*3+nb_filter[2], nb_filter[1], nb_filter[1])self.conv0_4 = VGGBlock(nb_filter[0]*4+nb_filter[1], nb_filter[0], nb_filter[0])if self.deep_supervision:self.final1 = nn.Conv2d(nb_filter[0], num_classes, kernel_size=1)self.final2 = nn.Conv2d(nb_filter[0], num_classes, kernel_size=1)self.final3 = nn.Conv2d(nb_filter[0], num_classes, kernel_size=1)self.final4 = nn.Conv2d(nb_filter[0], num_classes, kernel_size=1)else:self.final = nn.Conv2d(nb_filter[0], num_classes, kernel_size=1)def forward(self, input):# print('input:',input.shape)x0_0 = self.conv0_0(input)# print('x0_0:',x0_0.shape)x1_0 = self.conv1_0(self.pool(x0_0))# print('x1_0:',x1_0.shape)x0_1 = self.conv0_1(torch.cat([x0_0, self.up(x1_0)], 1))# print('x0_1:',x0_1.shape)x2_0 = self.conv2_0(self.pool(x1_0))# print('x2_0:',x2_0.shape)x1_1 = self.conv1_1(torch.cat([x1_0, self.up(x2_0)], 1))# print('x1_1:',x1_1.shape)x0_2 = self.conv0_2(torch.cat([x0_0, x0_1, self.up(x1_1)], 1))# print('x0_2:',x0_2.shape)x3_0 = self.conv3_0(self.pool(x2_0))# print('x3_0:',x3_0.shape)x2_1 = self.conv2_1(torch.cat([x2_0, self.up(x3_0)], 1))# print('x2_1:',x2_1.shape)x1_2 = self.conv1_2(torch.cat([x1_0, x1_1, self.up(x2_1)], 1))# print('x1_2:',x1_2.shape)x0_3 = self.conv0_3(torch.cat([x0_0, x0_1, x0_2, self.up(x1_2)], 1))# print('x0_3:',x0_3.shape)x4_0 = self.conv4_0(self.pool(x3_0))# print('x4_0:',x4_0.shape)x3_1 = self.conv3_1(torch.cat([x3_0, self.up(x4_0)], 1))# print('x3_1:',x3_1.shape)x2_2 = self.conv2_2(torch.cat([x2_0, x2_1, self.up(x3_1)], 1))# print('x2_2:',x2_2.shape)x1_3 = self.conv1_3(torch.cat([x1_0, x1_1, x1_2, self.up(x2_2)], 1))# print('x1_3:',x1_3.shape)x0_4 = self.conv0_4(torch.cat([x0_0, x0_1, x0_2, x0_3, self.up(x1_3)], 1))# print('x0_4:',x0_4.shape)if self.deep_supervision:output1 = self.final1(x0_1)output2 = self.final2(x0_2)output3 = self.final3(x0_3)output4 = self.final4(x0_4)return [output1, output2, output3, output4]else:output = self.final(x0_4)return output

损失函数

BCEDiceLoss:
  • 这个损失函数结合了二元交叉熵损失(Binary Cross Entropy, BCE)和 Dice Loss。
  • BCE 于衡量模型输出和真实标签之间的二值化像素级别匹配情况。
  • Dice Loss 用于量模型输出和真实标签之间的相似度,但这里采用了一种稍微不同的计算方式,即将 Dice Loss 作为 1 减去 Dice 相似度的平均值,这样得到的损失越小,说明相似度越高。
LovaszHingeLoss:
  • 这个损失函数采用的是 Lovasz-Hinge Loss,它是一种用于处理不平衡数据集的损失函数,尤其适用于像素级别的分类任务。
  • Lovasz-Hinge Loss 能够更好地处理类别不平衡和边界情况,相比于交叉熵损失,在处理不平衡数据时更加稳定。
    LovaszHingeLoss相关介绍
测试用例:

lovasz_losses.py 相关内容

"""
Lovasz-Softmax and Jaccard hinge loss in PyTorch
Maxim Berman 2018 ESAT-PSI KU Leuven (MIT License)
"""from __future__ import print_function, divisionimport torch
from torch.autograd import Variable
import torch.nn.functional as F
import numpy as nptry:from itertools import ifilterfalse
except ImportError:  # py3kfrom itertools import filterfalse as ifilterfalsedef lovasz_grad(gt_sorted):"""Computes gradient of the Lovasz extension w.r.t sorted errorsSee Alg. 1 in paper"""p = len(gt_sorted)gts = gt_sorted.sum()intersection = gts - gt_sorted.float().cumsum(0)union = gts + (1 - gt_sorted).float().cumsum(0)jaccard = 1. - intersection / unionif p > 1:  # cover 1-pixel casejaccard[1:p] = jaccard[1:p] - jaccard[0:-1]return jaccarddef iou_binary(preds, labels, EMPTY=1., ignore=None, per_image=True):"""IoU for foreground classbinary: 1 foreground, 0 background"""if not per_image:preds, labels = (preds,), (labels,)ious = []for pred, label in zip(preds, labels):intersection = ((label == 1) & (pred == 1)).sum()union = ((label == 1) | ((pred == 1) & (label != ignore))).sum()if not union:iou = EMPTYelse:iou = float(intersection) / float(union)ious.append(iou)iou = mean(ious)  # mean accross images if per_imagereturn 100 * ioudef iou(preds, labels, C, EMPTY=1., ignore=None, per_image=False):"""Array of IoU for each (non ignored) class"""if not per_image:preds, labels = (preds,), (labels,)ious = []for pred, label in zip(preds, labels):iou = []for i in range(C):if i != ignore:  # The ignored label is sometimes among predicted classes (ENet - CityScapes)intersection = ((label == i) & (pred == i)).sum()union = ((label == i) | ((pred == i) & (label != ignore))).sum()if not union:iou.append(EMPTY)else:iou.append(float(intersection) / float(union))ious.append(iou)ious = [mean(iou) for iou in zip(*ious)]  # mean accross images if per_imagereturn 100 * np.array(ious)# --------------------------- BINARY LOSSES ---------------------------def lovasz_hinge(logits, labels, per_image=True, ignore=None):"""Binary Lovasz hinge losslogits: [B, H, W] Variable, logits at each pixel (between -\infty and +\infty)labels: [B, H, W] Tensor, binary ground truth masks (0 or 1)per_image: compute the loss per image instead of per batchignore: void class id"""if per_image:loss = mean(lovasz_hinge_flat(*flatten_binary_scores(log.unsqueeze(0), lab.unsqueeze(0), ignore))for log, lab in zip(logits, labels))else:loss = lovasz_hinge_flat(*flatten_binary_scores(logits, labels, ignore))return lossdef lovasz_hinge_flat(logits, labels):"""Binary Lovasz hinge losslogits: [P] Variable, logits at each prediction (between -\infty and +\infty)labels: [P] Tensor, binary ground truth labels (0 or 1)ignore: label to ignore"""if len(labels) == 0:# only void pixels, the gradients should be 0return logits.sum() * 0.signs = 2. * labels.float() - 1.errors = (1. - logits * Variable(signs))errors_sorted, perm = torch.sort(errors, dim=0, descending=True)perm = perm.datagt_sorted = labels[perm]grad = lovasz_grad(gt_sorted)loss = torch.dot(F.relu(errors_sorted), Variable(grad))return lossdef flatten_binary_scores(scores, labels, ignore=None):"""Flattens predictions in the batch (binary case)Remove labels equal to 'ignore'"""scores = scores.view(-1)labels = labels.view(-1)if ignore is None:return scores, labelsvalid = (labels != ignore)vscores = scores[valid]vlabels = labels[valid]return vscores, vlabelsclass StableBCELoss(torch.nn.modules.Module):def __init__(self):super(StableBCELoss, self).__init__()def forward(self, input, target):neg_abs = - input.abs()loss = input.clamp(min=0) - input * target + (1 + neg_abs.exp()).log()return loss.mean()def binary_xloss(logits, labels, ignore=None):"""Binary Cross entropy losslogits: [B, H, W] Variable, logits at each pixel (between -\infty and +\infty)labels: [B, H, W] Tensor, binary ground truth masks (0 or 1)ignore: void class id"""logits, labels = flatten_binary_scores(logits, labels, ignore)loss = StableBCELoss()(logits, Variable(labels.float()))return loss# --------------------------- MULTICLASS LOSSES ---------------------------def lovasz_softmax(probas, labels, classes='present', per_image=False, ignore=None):"""Multi-class Lovasz-Softmax lossprobas: [B, C, H, W] Variable, class probabilities at each prediction (between 0 and 1).Interpreted as binary (sigmoid) output with outputs of size [B, H, W].labels: [B, H, W] Tensor, ground truth labels (between 0 and C - 1)classes: 'all' for all, 'present' for classes present in labels, or a list of classes to average.per_image: compute the loss per image instead of per batchignore: void class labels"""if per_image:loss = mean(lovasz_softmax_flat(*flatten_probas(prob.unsqueeze(0), lab.unsqueeze(0), ignore), classes=classes)for prob, lab in zip(probas, labels))else:loss = lovasz_softmax_flat(*flatten_probas(probas, labels, ignore), classes=classes)return lossdef lovasz_softmax_flat(probas, labels, classes='present'):"""Multi-class Lovasz-Softmax lossprobas: [P, C] Variable, class probabilities at each prediction (between 0 and 1)labels: [P] Tensor, ground truth labels (between 0 and C - 1)classes: 'all' for all, 'present' for classes present in labels, or a list of classes to average."""if probas.numel() == 0:# only void pixels, the gradients should be 0return probas * 0.C = probas.size(1)losses = []class_to_sum = list(range(C)) if classes in ['all', 'present'] else classesfor c in class_to_sum:fg = (labels == c).float()  # foreground for class cif (classes == 'present' and fg.sum() == 0):continueif C == 1:if len(classes) > 1:raise ValueError('Sigmoid output possible only with 1 class')class_pred = probas[:, 0]else:class_pred = probas[:, c]errors = (Variable(fg) - class_pred).abs()errors_sorted, perm = torch.sort(errors, 0, descending=True)perm = perm.datafg_sorted = fg[perm]losses.append(torch.dot(errors_sorted, Variable(lovasz_grad(fg_sorted))))return mean(losses)def flatten_probas(probas, labels, ignore=None):"""Flattens predictions in the batch"""if probas.dim() == 3:# assumes output of a sigmoid layerB, H, W = probas.size()probas = probas.view(B, 1, H, W)B, C, H, W = probas.size()probas = probas.permute(0, 2, 3, 1).contiguous().view(-1, C)  # B * H * W, C = P, Clabels = labels.view(-1)if ignore is None:return probas, labelsvalid = (labels != ignore)vprobas = probas[valid.nonzero().squeeze()]vlabels = labels[valid]return vprobas, vlabelsdef xloss(logits, labels, ignore=None):"""Cross entropy loss"""return F.cross_entropy(logits, Variable(labels), ignore_index=255)# --------------------------- HELPER FUNCTIONS ---------------------------
def isnan(x):return x != xdef mean(l, ignore_nan=False, empty=0):"""nanmean compatible with generators."""l = iter(l)if ignore_nan:l = ifilterfalse(isnan, l)try:n = 1acc = next(l)except StopIteration:if empty == 'raise':raise ValueError('Empty mean')return emptyfor n, v in enumerate(l, 2):acc += vif n == 1:return accreturn acc / n
import torch
import torch.nn as nn
import torch.nn.functional as F
from lovasz_losses import lovasz_hinge# __all__ = ['BCEDiceLoss', 'LovaszHingeLoss']class BCEDiceLoss(nn.Module):def __init__(self):super().__init__()def forward(self, input, target):bce = F.binary_cross_entropy_with_logits(input, target)smooth = 1e-5input = torch.sigmoid(input)num = target.size(0)input = input.view(num, -1)target = target.view(num, -1)intersection = (input * target)dice = (2. * intersection.sum(1) + smooth) / (input.sum(1) + target.sum(1) + smooth)dice = 1 - dice.sum() / numreturn 0.5 * bce + diceclass LovaszHingeLoss(nn.Module):def __init__(self):super().__init__()def forward(self, input, target):input = input.squeeze(1)target = target.squeeze(1)loss = lovasz_hinge(input, target, per_image=True)return lossif __name__ == '__main__':import torch# 假设模型输出和真实标签都是二值化的图像,大小为(1, H, W)output = torch.tensor([[[0.3, 0.7], [0.8, 0.6]]])  # 模型输出# output = output.round().long()target = torch.tensor([[[0, 1], [1, 0]]],dtype=torch.float)  # 真实标签bce_dice_loss = BCEDiceLoss()bce_dice = bce_dice_loss(output, target)lovasz_hinge_loss = LovaszHingeLoss()lovasz_hinge = lovasz_hinge_loss(output, target)print("BCE Dice Loss:", bce_dice)print("Lovasz Hinge Loss:", lovasz_hinge)

原理解释和数学公式:

BCEDiceLoss 原理:
  • BCE Dice Loss 结合了二元交叉熵损失和 Dice Loss。其数学表达式如下:

B C E _ D i c e _ L o s s = 0.5 × B C E + ( 1 − D i c e ) BCE\_Dice\_Loss = 0.5 \times BCE + (1 - Dice) BCE_Dice_Loss=0.5×BCE+(1Dice)

其中, B C E BCE BCE 表示二元交叉熵损失, D i c e Dice Dice 表示 Dice 相似度。这个损失函数的目标是最小化二元交叉熵损失和最大化 Dice 相似度,以达到更好的模型训练效果。

LovaszHingeLoss 原理:
  • Lovasz-Hinge Loss 是一种非平衡数据集上的损失函数,用于像素级别的分类任务。其数学表达式如下:

L o v a s z _ H i n g e _ L o s s = lovasz_hinge ( i n p u t , t a r g e t ) Lovasz\_Hinge\_Loss = \text{lovasz\_hinge}(input, target) Lovasz_Hinge_Loss=lovasz_hinge(input,target)

这里的 lovasz_hinge \text{lovasz\_hinge} lovasz_hinge 是一个函数,用于计算 Lovasz-Hinge Loss。

训练

√

评估函数

metrics.py

import numpy as np
import torch
import torch.nn.functional as Fdef iou_score(output, target):smooth = 1e-5if torch.is_tensor(output):output = torch.sigmoid(output).data.cpu().numpy()if torch.is_tensor(target):target = target.data.cpu().numpy()output_ = output > 0.5target_ = target > 0.5intersection = (output_ & target_).sum()union = (output_ | target_).sum()return (intersection + smooth) / (union + smooth)def dice_coef(output, target):smooth = 1e-5output = torch.sigmoid(output).view(-1).data.cpu().numpy()target = target.view(-1).data.cpu().numpy()intersection = (output * target).sum()return (2. * intersection + smooth) / \(output.sum() + target.sum() + smooth)if __name__ == '__main__':import numpy as npimport torch# 假设模型输出和真实标签都是二值化的图像,大小为(1, H, W)output = torch.tensor([[[0.3, 0.7], [0.8, 0.6]]])  # 模型输出target = torch.tensor([[[0, 1], [1, 0]]])  # 真实标签iou = iou_score(output, target)dice = dice_coef(output, target)print("IoU Score:", iou)print("Dice Coefficient:", dice)

在这里插入图片描述

IoU(Intersection over Union)评分函数原理

IoU 是一种常用的图像分割评价指标,它衡量了模型输出与真实标签之间的重程度。其数学公式如下:

I o U = T P T P + F P + F N IoU = \frac{{TP}}{{TP + FP + FN}} IoU=TP+FP+FNTP

其中, T P TP TP 表示真正例(模型正确预测为正样本的数量), F P FP FP 表示假正例(模型错误预测为正样本的数量), F N FN FN 表示假负例(模型错误预测为负样本的数量)。

Dice Coefficient评分函数原理

Dice Coefficient 也是一种常用的图像分割评价指标,衡量模型输出和真实标签之间的相似度。其数学公式如下:

D i c e = 2 × T P 2 × T P + F P + F N Dice = \frac{{2 \times TP}}{{2 \times TP + FP + FN}} Dice=2×TP+FP+FN2×TP

其中, T P TP TP 表示真正例, F P FP FP 表示假正例, F N FN FN 表示假负例,与 IoU 公式中的定义相同。

这两个评分函数都以模型的真正例为分子,而分母则是真正例、假正例和假负例的总和,以此来衡量模型预测结果与真实标签的相似程度。公式中的平滑因子用于避免分母为零的情况,增加了数值稳定性。


http://www.ppmy.cn/news/1522190.html

相关文章

如何创建训练数据集

在 HuggingFace 上创建数据集非常方便,创建完成之后,通过 API 可以方便的下载并使用数据集,在 Google Colab 上进行模型调优,下载数据集速度非常快,本文通过 Dataset 库创建一个简单的训练数据集。 首先安装数据集依赖…

黑马JavaWeb开发笔记14——Tomcat(介绍、安装与卸载、启动与关闭)、入门程序解析(起步依赖、SpringBoot父工程、内嵌Tomcat)

文章目录 前言一、Web服务器-Tomcat1. 简介1.1服务器概述1.2 Web服务器1.3 Tomcat 2. 基本使用2.1 下载2.2 安装与卸载2.3 启动与关闭2.4 常见问题 二、入门程序解析1. 起步依赖2. SpringBoot父工程3. 内嵌Tomcat 总结 前言 本篇文章是2023年最新黑马JavaWeb开发笔记14&#x…

高级java每日一道面试题-2024年9月03日-JVM篇-怎么判断对象是否可以被回收?

如果有遗漏,评论区告诉我进行补充 面试官: 怎么判断对象是否可以被回收? 我回答: 在Java中,判断一个对象是否可以被垃圾回收器(Garbage Collector, GC)回收,主要涉及到Java的内存管理和垃圾回收机制。Java采用自动内存管理机制…

【网络安全】IIS未授权访问敏感数据

未经许可,不得转载。 文章目录 正文攻击方法正文 IIS 是 Internet Information Services 的缩写,是微软开发的一个基于 Windows 的 Web 服务器。 HAProxy 是一个知名的高性能负载均衡器和代理服务器。它通常用于将流量分发到多个后端服务器,常与 Web 服务器(包括 IIS)一…

C# 特性与属性的区别

在 C# 中,"特性"(Attribute)和"属性"(Property)是两种不同的概念,它们在编程中扮演不同的角色: 属性(Property): 属性是类或结构的一部分…

生成对抗网络在数字病理学中的应用综述|文献精析·24-09-03

小罗碎碎念 本期推文主题:生成对抗网络 今天这篇推文于2024年发表于《Mod Pathol》,目前IF7.1,属于医学和病理学的一区文章。 作者角色姓名单位名称(中文)第一作者Shahd A. Alajaji马里兰大学牙科学院口腔医学与诊断科…

硬刚苹果还得是华为

文|琥珀食酒社 作者 | 璇子 牛皮啊 华为发三折叠不意外 意外的是 这各种翻转简直颠覆想象 市面上没见过这么能“翻转”的? 要不怎么说硬刚苹果 还得看华为 就跟你同天怎么了? 拼创新、拼技术、拼热度 你就说哪比你差吧&#xff1f…

[建模已更新]2024数学建模国赛高教社杯A题:“板凳龙” 闹元宵 思路代码文章助攻手把手保姆级

本系列专栏将包括两大块内容 第一块赛前真题和模型教学,包括至少8次真题实战教学,每期教学专栏的最底部会提供完整的资料百度网盘包括:真题、数据、可复现代码以及文章. 第二块包括赛中详细思路建模、代码的参考助攻, 会提供2024年高教社国赛A的全套参考内容(一般36h内更新完毕…

孤儿进程、僵尸进程、守护进程(精灵进程)

目录 一、孤儿进程 二、僵尸进程 三、守护进程&#xff08;精灵进程&#xff09; 一、孤儿进程 定义&#xff1a;孤儿进程是指那些其父进程已经结束&#xff0c;但它们依然在运行的进程 创建一个孤儿进程&#xff1a; #include <stdio.h> #include <stdlib.h> #in…

dockerfile部署fastapi项目

dockerfile部署fastapi项目 1、Dockerfile # 使用Python官方镜像作为基础镜像 FROM python:3.8-slim# 更新apt-get源并安装依赖 # RUN apt-get update -y && apt-get install -y git# 设置环境变量 ENV PYTHONDONTWRITEBYTECODE 1 ENV PYTHONUNBUFFERED 1# 创建工作目…

Python--列表简介

列表是什么 列表让你能够在⼀个地方存储成组的信息&#xff0c;其中既可以只包含几个元素&#xff0c;也可以包含数百万个元素。列表是新手可直接使用的最强大的Python 功能之⼀。 列表&#xff08;list&#xff09;是一种可变的序列类型&#xff0c;用于存储一系列有序的元素…

【全网首发】2024数学建模国赛C题39页word版成品论文【附带py+matlab双版本解题代码+可视化图表】

基于优化模型的农作物的种植策略 完整版成品py&#xff08;matlab&#xff09;代码解题在下面获取&#xff1a; 点击链接加入群聊【2024数学建模国赛资料汇总】&#xff1a;http://qm.qq.com/cgi-bin/qm/qr?_wv1027&klZncBILk30DuPRI1Bd8X-3Djv7ZVZyAv&authKeykKqNSS…

久久派安装启用USB摄像头(基于node-red)

久久派安装启用USB摄像头&#xff08;基于node-red&#xff09; 功能演示1、安装必要的节点2、程序讲解1、启动摄像头2、关闭摄像头3、获取久久派IP4、在UI界面显示摄像头内容5、摄像头抓拍 3、部署 文中所需网盘资料及讲解视频在文章末尾哦1。 本章使用的摄像头插件请参考久久…

4、Django Admin对自定义的计算字段进行排序

通常&#xff0c;Django会为模型属性字段&#xff0c;自动添加排序功能。当你添加计算字段时&#xff0c;Django不知道如何执行order_by&#xff0c;因此它不会在该字段上添加排序功能。 如果要在计算字段上添加排序&#xff0c;则必须告诉Django需要排序的内容。你可以通过在…

Qt/C++ 个人开源项目#串口助手(源码与发布链接)

一、项目概述 该串口助手工具基于Qt/C开发&#xff0c;专为简化串口通信调试与开发而设计&#xff0c;适合新手快速上手。工具具有直观的用户界面和丰富的功能&#xff0c;旨在帮助用户与串口设备建立可靠通信&#xff0c;便于调试、数据传输和分析。 二、主要功能 波特率&a…

Flutter集成Firebase中的 A/B Testing

前提 完成Flutter集成Firebase中的远程配置流程 A/B Test的使用流程 我们先通过远程配置设置变量&#xff0c;应用程序根据变量值展示不同的界面创建一个A/B Test实验&#xff0c;在实验中创建满足条件的用户才能加入到这个实验中&#xff0c;并且在A/B 实验中修改远程配置变…

shell 学习笔记:变量、字符串、注释

目录 1. 变量 1.1 定义使用变量 1.2 变量命名规则 1.3 只读变量 1.4 删除变量 1.5 变量类型 1.5.1 字符串变量 1.5.2 整数变量 1.5.3 数组变量 1.5.3.1 整数索引数组 1.5.3.2 关联数组 1.4 环境变量 1.5 特殊变量 2. 字符串 2.1 单引号字符串 2.2 双引…

使用NetBackup GUI 图形化进行oracle备份和恢复

转载 一、环境介绍&#xff1a; 这个实验都是在vmware workstation里完成的。由于NetBackup7只能装在64位的系统上&#xff0c;所以这里采用了64位的rhel5.5系统&#xff0c;以及oracle 10gr2 for linux_x64的软件包。数据库的数据文件存储在ASM中。安装rhel、oracle、netback…

C++字符串中的string类操作

愿我如星君如月&#xff0c;夜夜流光相皎洁。 ——《车逍遥篇》【宋】范成大 目录 正文&#xff1a; 主要特点&#xff1a; 基本操作&#xff1a; 代码演示&#xff1a; 总结&#xff1a; 今天我们接着上次的章节继续&#xff0c;这次我们来说一个为解决上个方法的缺陷而诞…

Linux 文件权限与属性管理

概述 Linux 系统是一种典型的多用户系统&#xff0c;不同的用户处于不同的地位&#xff0c;拥有不同的权限。为了保护系统的安全性&#xff0c;Linux 对不同用户访问同一文件&#xff08;包括目录文件&#xff09;的权限做了详细的规定。 文件属性查看 在 Linux 中&#xff0…