【学习打卡07】 可解释机器学习笔记之Shape+Lime代码实战

news2024/10/7 12:26:29

可解释机器学习笔记之Shape+Lime代码实战

文章目录

  • 可解释机器学习笔记之Shape+Lime代码实战
    • 基于Shapley值的可解释性分析
      • 使用Pytorch对MNIST分类可解释性分析
      • 使用shap的Deep Explainer进行可视化
      • 使用Pytorch对预训练ImageNet图像分类可解释性分析
        • 指定单个预测类别
        • 指定多个预测类别
        • 前k个预测类别
    • LIME代码实战
      • 对葡萄酒数据集二分类并进行LIME可解释性分析
        • 使用随机森林模型对葡萄酒数据集二分类
        • LIME可解释性分析
      • 对ImageNet预训练图像分类并进行LIME可解释性分析
        • LIME可解释性分析
    • 总结

首先非常感谢同济子豪兄拍摄的可解释机器学习公开课,并且免费分享,这门课程,包含人工智能可解释性、显著性分析领域的导论、算法综述、经典论文精读、代码实战、前沿讲座。由B站知名人工智能科普UP主“同济子豪兄”主讲。 课程主页: https://github.com/TommyZihao/zihao_course/blob/main/XAI 一起打开AI的黑盒子,洞悉AI的脑回路和注意力,解释它、了解它、改进它,进而信赖它。知其然,也知其所以然。这里给出链接,倡导大家一起学习, 别忘了给子豪兄点个关注哦。

学习GitHub 内容链接:
https://github.com/TommyZihao/zihao_course/tree/main/XAI

B站视频合集链接:
https://space.bilibili.com/1900783/channel/collectiondetail?sid=713364

基于Shapley值的可解释性分析

SHAP 属于模型事后解释的方法,它的核心思想是计算特征对模型输出的边际贡献,再从全局和局部两个层面对“黑盒模型”进行解释。SHAP构建一个加性的解释模型,所有的特征都视为“贡献者”。对于每个预测样本,模型都产生一个预测值,SHAP value就是该样本中每个特征所分配到的数值。基本思想:计算一个特征加入到模型时的边际贡献,然后考虑到该特征在所有的特征序列的情况下不同的边际贡献,取均值,即某该特征的SHAPbaseline value
SHAP(SHapley Additive exPlanation)是Python开发的一个"模型解释"包,可以解释任何机器学习模型的输出。

import torch
import torchvision
from torchvision import datasets, transforms, models
from torch import nn, optim
from torch.nn import functional as F
import os

import numpy as np
import json
from PIL import Image
# 使用torch-gpu
device = torch.device('cuda:0' if torch.cuda.is_available() else 'cpu')

shap代码实战

import shap

使用Pytorch对MNIST分类可解释性分析

用Pytorch构建简单的卷积神经网络,在MNIST手写数字数据集上,使用shap的Deep Explainer进行可解释性分析,并可视化每一张图像的每一个像素,对模型预测为每一个类别的影响。

# 构建卷积神经网络
class Net(nn.Module):
    def __init__(self):
        super(Net, self).__init__()

        self.conv_layers = nn.Sequential(
            nn.Conv2d(1, 10, kernel_size=5),
            nn.MaxPool2d(2),
            nn.ReLU(),
            nn.Conv2d(10, 20, kernel_size=5),
            nn.Dropout(),
            nn.MaxPool2d(2),
            nn.ReLU(),
        )
        self.fc_layers = nn.Sequential(
            nn.Linear(320, 50),
            nn.ReLU(),
            nn.Dropout(),
            nn.Linear(50, 10),
            nn.Softmax(dim=1)
        )

    def forward(self, x):
        x = self.conv_layers(x)
        x = x.view(-1, 320)
        x = self.fc_layers(x)
        return x
# 初始化模型
model = Net().to(device)
optimizer = optim.SGD(model.parameters(), lr=0.01, momentum=0.5)
# 加载MNIST数据集
train_dataset = datasets.MNIST('mnist_data', 
                               train=True, 
                               download=True,
                               transform=transforms.Compose([transforms.ToTensor()]))

test_dataset = datasets.MNIST('mnist_data', 
                               train=False, 
                               download=True,
                               transform=transforms.Compose([transforms.ToTensor()]))
# 设置dataloader
batch_size = 256
train_loader = torch.utils.data.DataLoader(
    train_dataset,
    batch_size=batch_size, 
    shuffle=True)

test_loader = torch.utils.data.DataLoader(
    test_dataset,
    batch_size=batch_size, 
    shuffle=True)
def train(model, device, train_loader, optimizer, epoch):
    # 训练一个 epoch
    model.train()
    for batch_idx, (data, target) in enumerate(train_loader):
        data, target = data.to(device), target.to(device)
        optimizer.zero_grad()
        output = model(data)
        loss = F.nll_loss(output.log(), target).to(device)
        loss.backward()
        optimizer.step()
        if batch_idx % 100 == 0:
            print('Train Epoch: {} [{}/{} ({:.0f}%)]\tLoss: {:.6f}'.format(
                epoch, batch_idx * len(data), len(train_loader.dataset),
                100. * batch_idx / len(train_loader), loss.item()))

def test(model, device, test_loader):
    # 测试一个 epoch
    model.eval()
    test_loss = 0
    correct = 0
    with torch.no_grad():
        for data, target in test_loader:
            data, target = data.to(device), target.to(device)
            output = model(data)
            test_loss += F.nll_loss(output.log(), target).item() # sum up batch loss
            pred = output.max(1, keepdim=True)[1] # get the index of the max log-probability
            correct += pred.eq(target.view_as(pred)).sum().item()

    test_loss /= len(test_loader.dataset)
    print('\nTest set: Average loss: {:.4f}, Accuracy: {}/{} ({:.0f}%)\n'.format(
        test_loss, correct, len(test_loader.dataset),
    100. * correct / len(test_loader.dataset)))
num_epochs = 5

for epoch in range(1, num_epochs + 1):
    train(model, device, train_loader, optimizer, epoch)
    test(model, device, test_loader)
Train Epoch: 1 [0/60000 (0%)]	Loss: 2.297472
Train Epoch: 1 [25600/60000 (43%)]	Loss: 2.202407
Train Epoch: 1 [51200/60000 (85%)]	Loss: 1.399053

Test set: Average loss: 0.0050, Accuracy: 7855/10000 (79%)

Train Epoch: 2 [0/60000 (0%)]	Loss: 1.234514
Train Epoch: 2 [25600/60000 (43%)]	Loss: 0.933571
Train Epoch: 2 [51200/60000 (85%)]	Loss: 0.774069

Test set: Average loss: 0.0025, Accuracy: 8880/10000 (89%)

Train Epoch: 3 [0/60000 (0%)]	Loss: 0.748982
Train Epoch: 3 [25600/60000 (43%)]	Loss: 0.621569
Train Epoch: 3 [51200/60000 (85%)]	Loss: 0.535523

Test set: Average loss: 0.0017, Accuracy: 9151/10000 (92%)

Train Epoch: 4 [0/60000 (0%)]	Loss: 0.569322
Train Epoch: 4 [25600/60000 (43%)]	Loss: 0.596375
Train Epoch: 4 [51200/60000 (85%)]	Loss: 0.552551

Test set: Average loss: 0.0014, Accuracy: 9330/10000 (93%)

Train Epoch: 5 [0/60000 (0%)]	Loss: 0.447947
Train Epoch: 5 [25600/60000 (43%)]	Loss: 0.550949
Train Epoch: 5 [51200/60000 (85%)]	Loss: 0.531695

Test set: Average loss: 0.0012, Accuracy: 9410/10000 (94%)

使用shap的Deep Explainer进行可视化

images, labels = next(iter(test_loader))
# 背景图像样本
background = images[:250]
background.shape
torch.Size([250, 1, 28, 28])
# 测试图像样本
test_images = images[250:254]
test_images.shape
torch.Size([4, 1, 28, 28])
# 初始化Deep Explainer
background = background.to(device)

e = shap.DeepExplainer(model, background)
# 计算每个类别、每张测试图像、每个像素,对应的 shap 值
shap_values = e.shap_values(test_images)
Using a non-full backward hook when the forward contains multiple autograd Nodes is deprecated and will be removed in future versions. This hook will be missing some grad_input. Please use register_full_backward_hook to get the documented behavior.
# shap 值
shap_numpy = [np.swapaxes(np.swapaxes(s, 1, -1), 1, 2) for s in shap_values]

# 测试图像
test_numpy = np.swapaxes(np.swapaxes(test_images.numpy(), 1, -1), 1, 2)
shap.image_plot(shap_numpy, -test_numpy)

png

红色代表 shap 正值:对模型预测为该类别有正向作用

蓝色代表 shap 负值:对模型预测为该类别有负向作用

使用Pytorch对预训练ImageNet图像分类可解释性分析

# 载入ImageNet预训练图像分类模型
model = torchvision.models.mobilenet_v2(weights=models.MobileNet_V2_Weights.DEFAULT, progress=False).eval().to(device)
with open('./data/imagenet_class_index.json') as file:
    class_names = [v[1] for v in json.load(file).values()]
# 测试图片
img_path = 'test_img/cat_dog.jpg'

img_pil = Image.open(img_path)
X = torch.Tensor(np.array(img_pil)).unsqueeze(0)
# 预处理
mean = [0.485, 0.456, 0.406]
std = [0.229, 0.224, 0.225]

def nhwc_to_nchw(x: torch.Tensor) -> torch.Tensor:
    if x.dim() == 4:
        x = x if x.shape[1] == 3 else x.permute(0, 3, 1, 2)
    elif x.dim() == 3:
        x = x if x.shape[0] == 3 else x.permute(2, 0, 1)
    return x

def nchw_to_nhwc(x: torch.Tensor) -> torch.Tensor:
    if x.dim() == 4:
        x = x if x.shape[3] == 3 else x.permute(0, 2, 3, 1)
    elif x.dim() == 3:
        x = x if x.shape[2] == 3 else x.permute(1, 2, 0)
    return x 
        

transform= [
    transforms.Lambda(nhwc_to_nchw),
    transforms.Resize(224),
    transforms.Lambda(lambda x: x*(1/255)),
    transforms.Normalize(mean=mean, std=std),
    transforms.Lambda(nchw_to_nhwc),
]

inv_transform= [
    transforms.Lambda(nhwc_to_nchw),
    transforms.Normalize(
        mean = (-1 * np.array(mean) / np.array(std)).tolist(),
        std = (1 / np.array(std)).tolist()
    ),
    transforms.Lambda(nchw_to_nhwc),
]

transform = torchvision.transforms.Compose(transform)
inv_transform = torchvision.transforms.Compose(inv_transform)
# 构建模型预测函数
def predict(img: np.ndarray) -> torch.Tensor:
    img = nhwc_to_nchw(torch.Tensor(img)).to(device)
    output = model(img)
    return output

def predict(img):
    img = nhwc_to_nchw(torch.Tensor(img)).to(device)
    output = model(img)
    return output
Xtr = transform(X)
out = predict(Xtr[0:1])
classes = torch.argmax(out, axis=1).detach().cpu().numpy()
print(f'Classes: {classes}: {np.array(class_names)[classes]}')
Classes: [239]: ['Bernese_mountain_dog']
# 构造输入图像
input_img = Xtr[0].unsqueeze(0)
batch_size = 50

n_evals = 5000 # 迭代次数越大,显著性分析粒度越精细,计算消耗时间越长

# 定义 mask,遮盖输入图像上的局部区域
masker_blur = shap.maskers.Image("blur(64, 64)", Xtr[0].shape)

# 创建可解释分析算法
explainer = shap.Explainer(predict, masker_blur, output_names=class_names)

指定单个预测类别

# 281:虎斑猫 tabby
shap_values = explainer(input_img, max_evals=n_evals, batch_size=batch_size, outputs=[281])
  0%|          | 0/4998 [00:00<?, ?it/s]


Partition explainer: 2it [00:13, 13.87s/it]                                                                                                                                                                  
# 整理张量维度
shap_values.data = inv_transform(shap_values.data).cpu().numpy()[0] # 原图
shap_values.values = [val for val in np.moveaxis(shap_values.values[0],-1, 0)] # shap值热力图
# 可视化
shap.image_plot(shap_values=shap_values.values,
                pixel_values=shap_values.data,
                labels=shap_values.output_names)
Clipping input data to the valid range for imshow with RGB data ([0..1] for floats or [0..255] for integers).

png

指定多个预测类别

# 232 边牧犬 border collie
# 281:虎斑猫 tabby
# 852 网球 tennis ball
# 288 豹子 leopard
shap_values = explainer(input_img, max_evals=n_evals, batch_size=batch_size, outputs=[232, 281, 852, 288])

# 整理张量维度
shap_values.data = inv_transform(shap_values.data).cpu().numpy()[0] # 原图
shap_values.values = [val for val in np.moveaxis(shap_values.values[0],-1, 0)] # shap值热力图

# 可视化
shap.image_plot(shap_values=shap_values.values,
                pixel_values=shap_values.data,
                labels=shap_values.output_names)
  0%|          | 0/4998 [00:00<?, ?it/s]


Partition explainer: 2it [00:12, 12.34s/it]                                                                                                                                                                  
Clipping input data to the valid range for imshow with RGB data ([0..1] for floats or [0..255] for integers).

png

前k个预测类别

topk = 5

shap_values = explainer(input_img, max_evals=n_evals, batch_size=batch_size, outputs=shap.Explanation.argsort.flip[:topk])

# 整理张量维度
shap_values.data = inv_transform(shap_values.data).cpu().numpy()[0] # 原图
shap_values.values = [val for val in np.moveaxis(shap_values.values[0],-1, 0)] # 各个类别的shap值热力图

# 可视化
shap.image_plot(shap_values=shap_values.values,
                pixel_values=shap_values.data,
                labels=shap_values.output_names
                )
  0%|          | 0/4998 [00:00<?, ?it/s]


Partition explainer: 2it [00:12, 12.28s/it]                                                                                                                                                                  
Clipping input data to the valid range for imshow with RGB data ([0..1] for floats or [0..255] for integers).

png

LIME代码实战

对葡萄酒数据集二分类并进行LIME可解释性分析

使用随机森林模型对葡萄酒数据集二分类

import numpy as np
import pandas as pd

import lime
from lime import lime_tabular
# 加载数据集
df = pd.read_csv('./data/wine.csv')
df.head()
fixed acidityvolatile aciditycitric acidresidual sugarchloridesfree sulfur dioxidetotal sulfur dioxidedensitypHsulphatesalcoholquality
07.00.270.3620.70.04545.0170.01.00103.000.458.8bad
16.30.300.341.60.04914.0132.00.99403.300.499.5bad
28.10.280.406.90.05030.097.00.99513.260.4410.1bad
37.20.230.328.50.05847.0186.00.99563.190.409.9bad
47.20.230.328.50.05847.0186.00.99563.190.409.9bad
from sklearn.model_selection import train_test_split

X = df.drop('quality', axis=1)
y = df['quality']

# 划分数据集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
from sklearn.ensemble import RandomForestClassifier

# 使用随机森林模型训练
model = RandomForestClassifier(random_state=42)
model.fit(X_train, y_train)
RandomForestClassifier(random_state=42)
score = model.score(X_test, y_test)
score
0.8887755102040816

LIME可解释性分析

# 初始化LIME可解释性分析算法
explainer = lime_tabular.LimeTabularExplainer(
    training_data=np.array(X_train), # 训练集特征,必须是 numpy 的 Array
    feature_names=X_train.columns, # 特征列名
    class_names=['bad', 'good'], # 预测类别名称
    mode='classification' # 分类模式
)
# 从测试集中选取一个样本,输入训练好的模型中预测,查看预测结果
idx = 3

data_test = np.array(X_test.iloc[idx]).reshape(1, -1)
prediction = model.predict(data_test)[0]
y_true = np.array(y_test)[idx]
print('测试集中的 {} 号样本, 模型预测为 {}, 真实类别为 {}'.format(idx, prediction, y_true))
测试集中的 3 号样本, 模型预测为 bad, 真实类别为 bad
# 可解释性分析
exp = explainer.explain_instance(
    data_row=X_test.iloc[idx], 
    predict_fn=model.predict_proba
)
exp.show_in_notebook(show_table=True)

对ImageNet预训练图像分类并进行LIME可解释性分析

img_path = './test_img/cat_dog.jpg'

img_pil = Image.open(img_path)
img_pil

[外链图片转存中…(img-jYx6rMWl-1671980645822)]

# 加载模型
model = models.inception_v3(weights=models.Inception_V3_Weights.DEFAULT).eval().to(device)
# 载入ImageNet-1000类别
idx2label, cls2label, cls2idx = [], {}, {}
with open('./data/imagenet_class_index.json', 'r') as read_file:
    class_idx = json.load(read_file)
    idx2label = [class_idx[str(k)][1] for k in range(len(class_idx))]
    cls2label = {class_idx[str(k)][0]: class_idx[str(k)][1] for k in range(len(class_idx))}
    cls2idx = {class_idx[str(k)][0]: k for k in range(len(class_idx))}    
# 预处理
trans_norm = transforms.Normalize(mean=[0.485, 0.456, 0.406],
                                  std=[0.229, 0.224, 0.225])

trans_A = transforms.Compose([
    transforms.Resize((256, 256)),
    transforms.CenterCrop(224),
    transforms.ToTensor(),
    trans_norm
    ])

trans_B = transforms.Compose([
        transforms.ToTensor(),
        trans_norm
    ])

trans_C = transforms.Compose([
    transforms.Resize((256, 256)),
    transforms.CenterCrop(224)
])
# 进行图像分类
input_tensor = trans_A(img_pil).unsqueeze(0).to(device)
pred_logits = model(input_tensor)
pred_softmax = F.softmax(pred_logits, dim=1)
top_n = pred_softmax.topk(5)
# 定义分类预测函数
def batch_predict(images):
    batch = torch.stack(tuple(trans_B(i) for i in images), dim=0)
    batch = batch.to(device)
    
    logits = model(batch)
    probs = F.softmax(logits, dim=1)
    return probs.detach().cpu().numpy()
test_pred = batch_predict([trans_C(img_pil)])
test_pred.squeeze().argmax()
231

LIME可解释性分析

from lime import lime_image
explainer = lime_image.LimeImageExplainer()
explanation = explainer.explain_instance(np.array(trans_C(img_pil)), 
                                         batch_predict, # 分类预测函数
                                         top_labels=5, 
                                         hide_color=0, 
                                         num_samples=8000) # LIME生成的邻域图像个数
  0%|          | 0/8000 [00:00<?, ?it/s]
explanation.top_labels[0]
231
from skimage.segmentation import mark_boundaries
import matplotlib.pyplot as plt
temp, mask = explanation.get_image_and_mask(explanation.top_labels[0], positive_only=False, num_features=20, hide_rest=False)
img_boundry = mark_boundaries(temp/255.0, mask)
plt.imshow(img_boundry)
plt.show()

png

temp, mask = explanation.get_image_and_mask(281, positive_only=False, num_features=20, hide_rest=False)
img_boundry = mark_boundaries(temp/255.0, mask)
plt.imshow(img_boundry)
plt.show()

png

绿色表示该区域对当前类别影响为正,红色表示该区域对当前类别影响为负

总结

在这次任务中,主要学习到了Shap和Lime工具包的使用,在图像分类的基础上去解释他,知其然还要知其所以然。使用CAM和Captum工具包,可以减少我们很多很多的代码量,并且能快速使用,快速应用在自己的任务中、

在经过一个多星期的学习,也是需要这种代码实战告诉我们,这些应用是全面且方方面面的,这样就不会空读理论,这样可以让我们有机会将理论和实践结合起来,希望后续能够将XAI和Lime运用到我的领域中,学习到更多的知识。

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

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

相关文章

Elasticsearch 核心技术(一):Elasticsearch 安装、配置、运行(Windows 版)

❤️ 个人主页&#xff1a;水滴技术 &#x1f680; 支持水滴&#xff1a;点赞&#x1f44d; 收藏⭐ 留言&#x1f4ac; &#x1f338; 订阅专栏&#xff1a;大数据核心技术从入门到精通 文章目录一、Elasticsearch 版本的选择二、下载 **Elasticsearch**三、安装 Elasticsear…

Springboot+Netty实现基于天翼物联网平台CTWing(AIOT)终端TCP协议(透传模式)-云服务端(IOT平台)

之前有文章用java实现了设备端和应用订阅端&#xff0c;那么我根据AIOT的协议也可以实现一个demo物联网平台端&#xff0c;这种简易的平台是实现自己搭建物联网平台的基础。 直接用代码 新建Springboot的maven项目&#xff0c;pom.xml文件导入依赖包&#xff08;用到了swagge…

UDP协议在Windows上使用示例

UDP(User Datagram Protocol&#xff0c;用户数据报协议)是无连接的&#xff0c;因此在两个进程通信前没有握手过程。UDP协议提供一种不可靠数据传送服务&#xff0c;也就是说&#xff0c;当进程将一个报文发送进UDP套接字时&#xff0c;UDP协议并不保证该报文将到达接收进程。…

过孔基础常识

过孔&#xff0c;一个绝大多数硬件工程师都听说过&#xff0c;但又并非真正了解的名词。了解的都知道&#xff0c;其在PCB板中其着至关重要的的作用。没有过孔的存在&#xff0c;很难画出一块完美的PCB板。所以呢&#xff0c;小编今日就带大家了解了解什么是过孔。 什么是过孔…

FCN代码及效果展示

1. 代码获取 代码地址: https://github.com/Le0v1n/ml_code/tree/main/Segmentation/FCN 2. 从头开始训练 2.1 测试平台 GPU&#xff1a;NVIDIA RTX 3070CPU: Intel I5-10400FRAM: 16GBOS: Windows 11Dataset: VOC2012Class num: 21(201)Batch size: 4Learning Rate: 0.1Ep…

嘉兴经开区第四届创新创业大赛总决赛成功举办

12月21日至12月22日&#xff0c;嘉兴经济技术开发区第四届创新创业大赛总决赛成功举办&#xff0c;经过激烈角逐最后共有10家企业分别获得大赛初创组和成长组的一二三等奖。 总决赛现场 嘉兴经开区第四届中国创新创业大赛于6月正式启动&#xff0c;陆续在嘉兴、成都、北京、西…

【详细学习SpringBoot源码之内嵌Tomcat启动原理分析编译部署Tomcat源码过程解析-9】

一.知识回顾 【0.SpringBoot专栏的相关文章都在这里哟&#xff0c;后续更多的文章内容可以点击查看】 【1.SpringBoot初识之Spring注解发展流程以及常用的Spring和SpringBoot注解】 【2.SpringBoot自动装配之SPI机制&SPI案例实操学习&SPI机制核心源码学习】 【3.详细学…

12-RabbitMq概述与工作模式深度剖析

MQ概述 MQ全称 Message Queue&#xff08;消息队列&#xff09;&#xff0c;是在消息的传输过程中保存消息的容器。多用于分布式系统之间进行通信。 MQ 的优势 应用解耦&#xff1a;提高系统容错性和可维护性 异步提速&#xff1a;提升用户体验和系统吞吐量 削峰填谷&#xff1…

unity中使用代码接绘制三维模型

一 模型的构成 在三维世界中&#xff0c;绘制一个模型并不是什么很复杂的问题。只要知道了基本原理一切需求便迎刃而解。 如下图所示&#xff0c;任何模型都是由点线面构成的&#xff0c;而面的最小单位是三角形。 任何一个多边形的面&#xff0c;都是由多个三角形构成的。比…

Web前端105天-day64-HTML5_CORE

HTML5CORE04 目录 前言 一、复习 二、WebSocket 三、服务器搭建 四、聊天室 五、defineProperty 5.1.初识defineProperty 5.2.配置多个属性 5.3.可配置 5.4.赋值监听 5.5.练习 5.6.计算属性 总结 前言 HTML5CORE04学习开始 一、复习 SVG: 利用HTML的 DOM 来绘制图…

PCB贴片机如何送料?

1.常见的贴片机供料器四种形式 http://www.sz-bjzn.com/1547.html 2.模块化设计SMT贴片机送料器的操作方法 3.淘宝 https://item.taobao.com/item.htm?spma230r.1.14.98.33e41823OZ1zzn&id579043582781&ns1&abbucket20#detail 不错&#xff1a;https://item.tao…

distinct与group by 去重

distinct与group by 去重distinct 特点&#xff1a;group by 特点&#xff1a;总结&#xff1a;mysql中常用去重复数据的方法是使用 distinct 或者group by &#xff0c;以上2种均能实现&#xff0c;但也有不同的地方。distinct 特点&#xff1a; 1、distinct 只能放在查询字段…

重新更新anaconda

更新anaconda问题阐述问题分析打开Anaconda Nvaigator打开文件所在位置复制文件所在路径找到此电脑或者打开设置找到高级系统设置环境变量添加环境变量打开scripts文件修改成功再一次启动感谢观看今天手贱,不小心删掉的anaconda,我想一不做二不休,直接重新重装了,就找到了anaco…

经典SQL语句大全(基础、提升、技巧、数据开发、基本函数)

目录 前言 正文 第一章&#xff1a;基础 第二章&#xff1a;提升 第三章&#xff1a;技巧 第四章&#xff1a;数据开发-经典​​​​​​​ 第五章&#xff1a;SQL Server基本函数 第六章&#xff1a;常识 第七章&#xff1a;SQLServer2000 同步复制技术实现步骤 总结…

juc-4-synchronized原理

目录 1、synchronized 作用于静态方法 总结 ​编辑 案例 静态成员变量 (synchronized锁非静态方法) 案例 静态成员变量 (synchronized锁静态方法 或 直接锁类) 2、监视器锁(monitor) 2.1 synchronized怎么实现的线程安全呢&#xff1f; 3、JDK6 synchronized 的优化 3.1 C…

互联网技术不再是统领,当下正在发生着一场深刻的变革

拥抱实体经济&#xff0c;绝对是当下互联网玩家们的首要选择。无论是头部的互联网企业来讲&#xff0c;还是新生的互联网玩家而言&#xff0c;它们都不约而同地将关注的焦点聚焦在了这样一个方向上。   透过这一点&#xff0c;我们可以非常明显地感受到&#xff0c;一个全新的…

圣诞节,记录前行中跨过的2022

2022年&#xff0c;我人生的第二十四年&#xff0c;是我大学生活的最后一年&#xff0c;是我职场生涯的第一年&#xff0c;这一年从学生到打工人&#xff0c;从实习生到职场员工&#xff0c;变化了许多&#xff0c;做了许多&#xff0c;收获了许多&#xff0c;同时也成长了许多…

m自适应FSK解调系统误码率matlab仿真

目录 1.算法描述 2.仿真效果预览 3.MATLAB核心程序 4.完整MATLAB 1.算法描述 FSK信号的解调也有非相干和相干两种&#xff0c;FSK信号可以看作是用两个频率源交替传输得到的&#xff0c;所以FSK的接收机由两个并联的ASK接收机组成。 (1)相干解调 相干解调是利用乘法器&…

Minecraft(我的世界) Fabric 1.19.3 服务器搭建教程

Debian系统使用MCSManager9面板搭建MC Java版MOD服务器的教程&#xff0c;本教程用的Fabric1.19.3服务端&#xff0c;用其他服务端的也可以参考一下。 视频教程&#xff1a;https://www.bilibili.com/video/BV1Zd4y1h7zG/ 我的世界(MC) Fabric 1.19.3 开服教程&#xff0c;新手…

IDEA Git 选项栏各项功能详解

IDEA Git 选项栏各项功能详解 如图所示 Copy Revision Number 顾名思义 拷贝当前版本号 到剪切板 a8e4b86ce9ca01968629504a6e19b4b99d76a853 Create Patch 在 git 日志中选择要创建补丁的commit&#xff0c;右键选择Create Patch...同一个文件在多次commit中都存在&#xff…