[pytorch] MedMNIST 3D医学数据分类
医学数据集的资源往往是比较难找的,3d数据集公开的更少。而MedMNIST v2,是一个大规模的类似 MNIST 的标准化生物医学图像集合,包括 12 个 2D 数据集和 6 个 3D 数据集。所有图像都被预处理成 28 x 28 (2D) 或 28 x 28 x 28 (3D) 并带有相应的分类标签,因此用户不需要背景知识。MedMNIST v2 涵盖生物医学图像中的主要数据模式,旨在对具有各种数据规模(从 100 到 100,000)和不同任务(二元/多类、序数回归和多标签)的轻量级 2D 和 3D 图像执行分类。
我们可以使用它来测试我们的3d网络等等。
数据集介绍:MedMNIST v2: A Large-Scale Lightweight Benchmark for 2D and 3D Biomedical Image Classification
github:MedMNIST
我们这里分析两个3d数据集 OrganMNIST3D 和 VesselMNIST3D,分别实现多分类和二分类。


from tqdm import tqdm
import numpy as np
import torch
import torch.nn as nn
import torch.optim as optim
import torch.utils.data as data
import torchvision.transforms as transforms
import medmnist
from medmnist import INFO, Evaluator
import os
import time
import torch.nn as nn
import torch
import torchvision.transforms as transforms
from PIL import Image
from matplotlib import pyplot as plt
import torchvision.models as models
import torchsummary
import time
from torch.optim.lr_scheduler import ExponentialLR
使用tensorboard记录结果
from torch.utils.tensorboard import SummaryWriter
summaryWriter = SummaryWriter("./logs/")
batch_size = 256
数据处理
class Transform3D:
def __init__(self, mul=None):
self.mul = mul
def __call__(self, voxel):
if self.mul == '0.5':
voxel = voxel * 0.5
elif self.mul == 'random':
voxel = voxel * np.random.uniform()
return voxel.astype(np.float32)
下载数据
print('==> Preparing data...')
train_transform = Transform3D(mul='random')
eval_transform = Transform3D(mul='0.5')
data_flag = 'organmnist3d' # Multi-Class (11)
download = True
info = INFO[data_flag]
DataClass = getattr(medmnist, info['python_class'])
# load the data
train_dataset = DataClass(split='train', transform=train_transform, download=download)
val_dataset = DataClass(split='val', transform=eval_transform, download=download)
test_dataset = DataClass(split='test', transform=eval_transform, download=download)
3d数据可视化函数
def draw_oct(volume, type_volume = 'np',canal_first = False):
if type_volume == 'np':
if canal_first == False:
print("taille du volume = %s (%s)"%(volume.shape,type_volume))
slice_h_n, slice_d_n , slice_w_n = int(volume.shape[0]/2),int(volume.shape[1]/2),int(volume.shape[2]/2)
slice_h = volume[slice_h_n,:,:,:]
slice_d = volume[:,slice_d_n,:,:]
slice_w = volume[:,:,slice_w_n,:]
slice_h = Image.fromarray(np.squeeze(slice_h))
slice_d = Image.fromarray(np.squeeze(slice_d))
slice_w = Image.fromarray(np.squeeze(slice_w))
plt.figure(figsize=(21,7))
plt.subplot(1, 3, 1)
plt.imshow(slice_h)
plt.title(slice_h.size)
plt.axis('off')
plt.subplot(1, 3, 2)
plt.imshow(slice_d)
plt.title(slice_d.size)
plt.axis('off')
plt.subplot(1, 3, 3)
plt.imshow(slice_w)
plt.title(slice_w.size)
plt.axis('off')
if canal_first == True:
print("taille du volume = %s (%s)"%(volume.shape,type_volume))
slice_h_n, slice_d_n , slice_w_n = int(volume.shape[1]/2),int(volume.shape[2]/2),int(volume.shape[3]/2)
slice_h = volume[:,slice_h_n,:,:]
slice_d = volume[:,:,slice_d_n,:]
slice_w = volume[:,:,:,slice_w_n]
slice_h = Image.fromarray(np.squeeze(slice_h))
slice_d = Image.fromarray(np.squeeze(slice_d))
slice_w = Image.fromarray(np.squeeze(slice_w))
plt.figure(figsize=(21,7))
plt.subplot(1, 3, 1)
plt.imshow(slice_h)
plt.title(slice_h.size)
plt.axis('off')
plt.subplot(1, 3, 2)
plt.imshow(slice_d)
plt.title(slice_d.size)
plt.axis('off')
plt.subplot(1, 3, 3)
plt.imshow(slice_w)
plt.title(slice_w.size)
plt.axis('off')
if type_volume == 'tensor':
if canal_first == False:
print("taille du volume = %s (%s)"%(volume.shape,type_volume))
slice_h_n, slice_d_n , slice_w_n = int(volume.shape[0]/2),int(volume.shape[1]/2),int(volume.shape[2]/2)
slice_h = volume[slice_h_n,:,:,:].numpy()
slice_d = volume[:,slice_d_n,:,:].numpy()
slice_w = volume[:,:,slice_w_n,:].numpy()
slice_h = Image.fromarray(np.squeeze(slice_h))
slice_d = Image.fromarray(np.squeeze(slice_d))
slice_w = Image.fromarray(np.squeeze(slice_w))
plt.figure(figsize=(21,7))
plt.subplot(1, 3, 1)
plt.imshow(slice_h)
plt.title(slice_h.size)
plt.axis('off')
plt.subplot(1, 3, 2)
plt.imshow(slice_d)
plt.title(slice_d.size)
plt.axis('off')
plt.subplot(1, 3, 3)
plt.imshow(slice_w)
plt.title(slice_w.size)
plt.axis('off')
if canal_first == True:
slice_h_n, slice_d_n , slice_w_n = int(volume.shape[1]/2),int(volume.shape[2]/2),int(volume.shape[3]/2)
slice_h = volume[:,slice_h_n,:,:].numpy()
slice_d = volume[:,:,slice_d_n,:].numpy()
slice_w = volume[:,:,:,slice_w_n].numpy()
slice_h = Image.fromarray(np.squeeze(slice_h))
slice_d = Image.fromarray(np.squeeze(slice_d))
slice_w = Image.fromarray(np.squeeze(slice_w))
plt.figure(figsize=(21,7))
plt.subplot(1, 3, 1)
plt.imshow(slice_h)
plt.title(slice_h.size)
plt.axis('off')
plt.subplot(1, 3, 2)
plt.imshow(slice_d)
plt.title(slice_d.size)
plt.axis('off')
plt.subplot(1, 3, 3)
plt.imshow(slice_w)
plt.title(slice_w.size)
plt.axis('off')
x, y = train_dataset[0]
print(x.shape, y)
draw_oct(x*500,type_volume = 'np',canal_first = True)

产生dataloader
train_loader = data.DataLoader(dataset=train_dataset,
batch_size=batch_size,
shuffle=True)
val_loader = data.DataLoader(dataset=val_dataset,
batch_size=batch_size,
shuffle=False)
test_loader = data.DataLoader(dataset=test_dataset,
batch_size=batch_size,
shuffle=False)
for x, y in train_loader:
print(x.shape, y.shape)
break
# torch.Size([256, 1, 28, 28, 28]) torch.Size([256, 1])
我使用了MedicalNet的预训练resnet模型。
mednet的网络是用于分割任务的,所以其结构是resnet提取特征图像,最后加反卷积层做分割。我们的任务是分类,于是我将最后的反卷积层替换为分类层。
resnet3d预训练模型参数可以从官方的github上下载,然后直接像下面一样加载即可。注意:需要使用mednet项目代码中的models文件夹,将这个文件夹和要加载的预训练参数复制到自己的项目中。
from models import resnet
device = torch.device('cuda:0' if torch.cuda.is_available() else 'cpu')
print('device =',device)
print(torch.cuda.get_device_name(0))
def generate_model(model_type='resnet', model_depth=50,
input_W=224, input_H=224, input_D=224, resnet_shortcut='B',
no_cuda=False, gpu_id=[0],
pretrain_path = 'pretrain/resnet_50.pth',
nb_class=1):
assert model_type in [
'resnet'
]
if model_type == 'resnet':
assert model_depth in [10, 18, 34, 50, 101, 152, 200]
if model_depth == 10:
model = resnet.resnet10(
sample_input_W=input_W,
sample_input_H=input_H,
sample_input_D=input_D,
shortcut_type=resnet_shortcut,
no_cuda=no_cuda,
num_seg_classes=1)
fc_input = 256
elif model_depth == 18:
model = resnet.resnet18(
sample_input_W=input_W,
sample_input_H=input_H,
sample_input_D=input_D,
shortcut_type=resnet_shortcut,
no_cuda=no_cuda,
num_seg_classes=1)
fc_input = 512
elif model_depth == 34:
model = resnet.resnet34(
sample_input_W=input_W,
sample_input_H=input_H,
sample_input_D=input_D,
shortcut_type=resnet_shortcut,
no_cuda=no_cuda,
num_seg_classes=1)
fc_input = 512
elif model_depth == 50:
model = resnet.resnet50(
sample_input_W=input_W,
sample_input_H=input_H,
sample_input_D=input_D,
shortcut_type=resnet_shortcut,
no_cuda=no_cuda,
num_seg_classes=1)
fc_input = 2048
elif model_depth == 101:
model = resnet.resnet101(
sample_input_W=input_W,
sample_input_H=input_H,
sample_input_D=input_D,
shortcut_type=resnet_shortcut,
no_cuda=no_cuda,
num_seg_classes=1)
fc_input = 2048
elif model_depth == 152:
model = resnet.resnet152(
sample_input_W=input_W,
sample_input_H=input_H,
sample_input_D=input_D,
shortcut_type=resnet_shortcut,
no_cuda=no_cuda,
num_seg_classes=1)
fc_input = 2048
elif model_depth == 200:
model = resnet.resnet200(
sample_input_W=input_W,
sample_input_H=input_H,
sample_input_D=input_D,
shortcut_type=resnet_shortcut,
no_cuda=no_cuda,
num_seg_classes=1)
fc_input = 2048
model.conv_seg = nn.Sequential(nn.AdaptiveAvgPool3d((1, 1, 1)), nn.Flatten(),
nn.Linear(in_features=fc_input, out_features=nb_class, bias=True))
if not no_cuda:
if len(gpu_id) > 1:
model = model.cuda()
model = nn.DataParallel(model, device_ids=gpu_id)
net_dict = model.state_dict()
else:
import os
os.environ["CUDA_VISIBLE_DEVICES"]=str(gpu_id[0])
model = model.cuda()
model = nn.DataParallel(model, device_ids=None)
net_dict = model.state_dict()
else:
net_dict = model.state_dict()
print('loading pretrained model {}'.format(pretrain_path))
pretrain = torch.load(pretrain_path)
pretrain_dict = {k: v for k, v in pretrain['state_dict'].items() if k in net_dict.keys()}
# k 是每一层的名称,v是权重数值
net_dict.update(pretrain_dict) #字典 dict2 的键/值对更新到 dict 里。
model.load_state_dict(net_dict) #model.load_state_dict()函数把加载的权重复制到模型的权重中去
print("-------- pre-train model load successfully --------")
return model
model = generate_model(model_type='resnet', model_depth=50,
input_W=224, input_H=224, input_D=224, resnet_shortcut='B',
no_cuda=False, gpu_id=[0],
pretrain_path = './resnet_50_23dataset.pth',
nb_class=11)

optimizer = torch.optim.Adam(model.parameters(), lr=1e-3)
criterion = nn.CrossEntropyLoss()
scheduler = ExponentialLR(optimizer, gamma=0.99)
num_epochs = 800
total_step = len(train_loader)
time_list = []
for epoch in range(num_epochs):
start = time.time()
per_epoch_loss = 0
num_correct= 0
val_num_correct = 0
model.train()
with torch.enable_grad():
for x,label in tqdm(train_loader):
x = x.to(device)
label = label.to(device)
label = torch.squeeze(label)# label的形状是 [256,1] 要将其变成 [256]
# Forward pass
logits = model(x)
loss = criterion(logits, label)
per_epoch_loss += loss.item()
# Backward and optimize
optimizer.zero_grad()
loss.backward()
optimizer.step()
pred = logits.argmax(dim=1)
num_correct += torch.eq(pred, label).sum().float().item()
print("Train Epoch: {}\t Loss: {:.6f}\t Acc: {:.6f}".format(epoch,per_epoch_loss/total_step,num_correct/len(train_loader.dataset)))
summaryWriter.add_scalars('loss', {"loss":(per_epoch_loss/total_step)}, epoch)
summaryWriter.add_scalars('acc', {"acc":num_correct/len(train_loader.dataset)}, epoch)
model.eval()
with torch.no_grad():
for x,label in tqdm(val_loader):
x = x.to(device)
label = label.to(device)
label = torch.squeeze(label)
# Forward pass
logits = model(x)
pred = logits.argmax(dim=1)
val_num_correct += torch.eq(pred, label).sum().float().item()
print("val Epoch: {}\t Acc: {:.6f}".format(epoch,num_correct/len(train_loader.dataset)))
summaryWriter.add_scalars('acc', {"val_acc":val_num_correct/len(val_loader.dataset)}, epoch)
summaryWriter.add_scalars('time', {"time":(time.time() - start)}, epoch)
scheduler.step()
最后让我们看一下训练结果。

大体上和多分类任务是一样的,有几段代码需要修改。
数据下载
data_flag = 'vesselmnist3d' # Binary-Class (2)
download = True
info = INFO[data_flag]
DataClass = getattr(medmnist, info['python_class'])
# load the data
train_dataset = DataClass(split='train', transform=train_transform, download=download)
val_dataset = DataClass(split='val', transform=eval_transform, download=download)
test_dataset = DataClass(split='test', transform=eval_transform, download=download)
可视化

加载模型
model = generate_model(model_type='resnet', model_depth=50,
input_W=28, input_H=28, input_D=28, resnet_shortcut='B',
no_cuda=False, gpu_id=[0],
pretrain_path = './resnet_50_23dataset.pth',
nb_class=1)
训练参数,二分类使用BCEWithLogitsLoss
optimizer = torch.optim.Adam(model.parameters(), lr=1e-3)
criterion = torch.nn.BCEWithLogitsLoss(pos_weight=torch.tensor([10.0])).cuda() #分类不均衡
scheduler = ExponentialLR(optimizer, gamma=0.99)
num_epochs = 1200
我们使用acc和auc作为指标
from sklearn.metrics import roc_curve
from sklearn.metrics import auc
for epoch in range(num_epochs):
start = time.time()
per_epoch_loss = 0
num_correct= 0
score_list = []
label_list = []
val_num_correct = 0
val_score_list = []
val_label_list = []
model.train()
with torch.enable_grad():
for x,label in tqdm(train_loader):
x = x.float()
x = x.to(device)
label = label.to(device)
label = torch.squeeze(label)
label_list.extend(label.cpu().numpy())
#print(label_list)
# Forward pass
logits = model(x)
logits = logits.reshape([label.cpu().numpy().shape[0]])
prob_out = nn.Sigmoid()(logits)
#print(logits.shape)
pro_list = prob_out.detach().cpu().numpy()
#print(pro_list)
#print(abc)
#print(pro_list)
for i in range(pro_list.shape[0]):
if (pro_list[i] > 0.5) == label.cpu().numpy()[i]:
num_correct += 1
score_list.extend(pro_list)
loss = criterion(logits, label.float())
per_epoch_loss += loss.item()
# Backward and optimize
optimizer.zero_grad()
loss.backward()
optimizer.step()
#pred = logits.argmax(dim=1)
#num_correct += torch.eq(pred, label).sum().float().item()
score_array = np.array(score_list)
label_array = np.array(label_list)
fpr_keras_1, tpr_keras_1, thresholds_keras_1 = roc_curve(label_array, score_array)
auc_keras_1 = auc(fpr_keras_1,tpr_keras_1)
print("Train EVpoch: {}\t Loss: {:.6f}\t Acc: {:.6f} AUC: {:.6f} ".format(epoch,per_epoch_loss/len(train_loader),num_correct/len(train_loader.dataset),auc_keras_1))
summaryWriter.add_scalars('loss', {"loss":(per_epoch_loss/len(train_loader))}, epoch)
summaryWriter.add_scalars('acc', {"acc":num_correct/len(train_loader.dataset)}, epoch)
summaryWriter.add_scalars('auc', {"auc":auc_keras_1}, epoch)
model.eval()
with torch.no_grad():
for x,label in tqdm(val_loader):
x = x.float()
x = x.to(device)
label = label.to(device)
#label_n = label.cpu().numpy()
val_label_list.extend(label.cpu().numpy())
# Forward pass
logits = model(x)
logits = logits.reshape([label.cpu().numpy().shape[0]])
prob_out = nn.Sigmoid()(logits)
#print(logits.shape)
pro_list = prob_out.detach().cpu().numpy()
#print(pro_list)
for i in range(pro_list.shape[0]):
if (pro_list[i] > 0.5) == label.cpu().numpy()[i]:
val_num_correct += 1
val_score_list.extend(pro_list)
score_array = np.array(val_score_list)
label_array = np.array(val_label_list)
fpr_keras_1, tpr_keras_1, thresholds_keras_1 = roc_curve(label_array, score_array)
auc_keras_1 = auc(fpr_keras_1,tpr_keras_1)
print("val Epoch: {}\t Acc: {:.6f} AUC: {:.6f} ".format(epoch,val_num_correct/len(val_loader.dataset),auc_keras_1))
summaryWriter.add_scalars('acc', {"val_acc":val_num_correct/len(val_loader.dataset)}, epoch)
summaryWriter.add_scalars('auc', {"val_auc":auc_keras_1}, epoch)
summaryWriter.add_scalars('time', {"time":(time.time() - start)}, epoch)
scheduler.step()
#filepath = "./weights"
#folder = os.path.exists(filepath)
#if not folder:
# # 判断是否存在文件夹如果不存在则创建为文件夹
# os.makedirs(filepath)
#path = './weights/model' + str(epoch) + '.pth'
#torch.save(model.state_dict(), path)
结果


我想在Ruby中创建一个用于开发目的的极其简单的Web服务器(不,不想使用现成的解决方案)。代码如下:#!/usr/bin/rubyrequire'socket'server=TCPServer.new('127.0.0.1',8080)whileconnection=server.acceptheaders=[]length=0whileline=connection.getsheaders想法是从命令行运行这个脚本,提供另一个脚本,它将在其标准输入上获取请求,并在其标准输出上返回完整的响应。到目前为止一切顺利,但事实证明这真的很脆弱,因为它在第二个请求上中断并出现错误:/usr/b
无论您是想搭建桌面端、WEB端或者移动端APP应用,HOOPSPlatform组件都可以为您提供弹性的3D集成架构,同时,由工业领域3D技术专家组成的HOOPS技术团队也能为您提供技术支持服务。如果您的客户期望有一种在多个平台(桌面/WEB/APP,而且某些客户端是“瘦”客户端)快速、方便地将数据接入到3D应用系统的解决方案,并且当访问数据时,在各个平台上的性能和用户体验保持一致,HOOPSPlatform将帮助您完成。利用HOOPSPlatform,您可以开发在任何环境下的3D基础应用架构。HOOPSPlatform可以帮您打造3D创新型产品,HOOPSSDK包含的技术有:快速且准确的CAD
本教程将在Unity3D中混合Optitrack与数据手套的数据流,在人体运动的基础上,添加双手手指部分的运动。双手手背的角度仍由Optitrack提供,数据手套提供双手手指的角度。 01 客户端软件分别安装MotiveBody与MotionVenus并校准人体与数据手套。MotiveBodyMotionVenus数据手套使用、校准流程参照:https://gitee.com/foheart_1/foheart-h1-data-summary.git02 数据转发打开MotiveBody软件的Streaming,开始向Unity3D广播数据;MotionVenus中设置->选项选择Unit
Unity自动旋转动画1.开门需要门把手先动,门再动2.关门需要门先动,门把手再动3.中途播放过程中不可以再次进行操作觉得太复杂?查看我的文章开关门简易进阶版效果:如果这个门可以直接打开的话,就不需要放置"门把手"如果门把手还有钥匙需要旋转,那就可以把钥匙放在门把手的"门把手",理论上是可以无限套娃的可调整参数有:角度,反向,轴向,速度运行时点击Test进行测试自己写的代码比较垃圾,命名与结构比较拉,高手轻点喷,新手有类似的需求可以拿去做参考上代码usingSystem.Collections;usingSystem.Collections.Generic;usingUnityEngine;u
之前说过10之后的版本没有3dScan了,所以还是9.8的版本或者之前更早的版本。 3d物体扫描需要先下载扫描的APK进行扫面。首先要在手机上装一个扫描程序,扫描现实中的三维物体,然后上传高通官网,在下载成UnityPackage类型让Unity能够使用这个扫描程序可以从高通官网上进行下载,是一个安卓程序。点到Tools往下滑,找到VuforiaObjectScanner下载后解压数据线连接手机,将apk文件拷入手机安装然后刚才解压文件中的Media文件夹打开,两个PDF图打印第一张A4-ObjectScanningTarget.pdf,主要是用来辅助扫描的。好了,接下来就是扫描三维物体。将瓶
网络编程套接字网络编程基础知识理解源`IP`地址和目的`IP`地址理解源MAC地址和目的MAC地址认识端口号理解端口号和进程ID理解源端口号和目的端口号认识`TCP`协议认识`UDP`协议网络字节序socket编程接口`sockaddr``UDP`网络程序服务器端代码逻辑:需要用到的接口服务器端代码`udp`客户端代码逻辑`udp`客户端代码`TCP`网络程序服务器代码逻辑多个版本服务器单进程版本多进程版本多线程版本线程池版本服务器端代码客户端代码逻辑客户端代码TCP协议通讯流程TCP协议的客户端/服务器程序流程三次握手(建立连接)数据传输四次挥手(断开连接)TCP和UDP对比网络编程基础知识
是否可以在不实际下载文件的情况下检查文件是否存在?我有这么大的(~40mb)文件,例如:http://mirrors.sohu.com/mysql/MySQL-6.0/MySQL-6.0.11-0.glibc23.src.rpm这与ruby不严格相关,但如果发件人可以设置内容长度就好了。RestClient.get"http://mirrors.sohu.com/mysql/MySQL-6.0/MySQL-6.0.11-0.glibc23.src.rpm",headers:{"Content-Length"=>100} 最佳答案
我在这方面尝试了很多URL,在我遇到这个特定的之前,它们似乎都很好:require'rubygems'require'nokogiri'require'open-uri'doc=Nokogiri::HTML(open("http://www.moxyst.com/fashion/men-clothing/underwear.html"))putsdoc这是结果:/Users/macbookair/.rvm/rubies/ruby-2.0.0-p481/lib/ruby/2.0.0/open-uri.rb:353:in`open_http':404NotFound(OpenURI::HT
关闭。这个问题不符合StackOverflowguidelines.它目前不接受答案。要求我们推荐或查找工具、库或最喜欢的场外资源的问题对于StackOverflow来说是偏离主题的,因为它们往往会吸引自以为是的答案和垃圾邮件。相反,describetheproblem以及迄今为止为解决该问题所做的工作。关闭9年前。Improvethisquestion是否有适用于这些的3d游戏引擎?
深度学习12.CNN经典网络VGG16一、简介1.VGG来源2.VGG分类3.不同模型的参数数量4.3x3卷积核的好处5.关于学习率调度6.批归一化二、VGG16层分析1.层划分2.参数展开过程图解3.参数传递示例4.VGG16各层参数数量三、代码分析1.VGG16模型定义2.训练3.测试一、简介1.VGG来源VGG(VisualGeometryGroup)是一个视觉几何组在2014年提出的深度卷积神经网络架构。VGG在2014年ImageNet图像分类竞赛亚军,定位竞赛冠军;VGG网络采用连续的小卷积核(3x3)和池化层构建深度神经网络,网络深度可以达到16层或19层,其中VGG16和VGG