医工互联

 找回密码
 注册[Register]

手机动态码快速登录

手机号快速登录

微信登录

微信扫一扫,快速登录

QQ登录

只需一步,快速开始

查看: 312|回复: 0
收起左侧

【医学图像分析与理解课内实验】医学图像分类实验

[复制链接]

  离线 

发表于 2022-10-22 10:24:03 来自手机 | 显示全部楼层 |阅读模式 <
实验目的

任意选择分类算法,实现乳腺癌分类。要求所有分类算法均自己实现。
下图是一个良性样本:
112501ul1quy1q4200pd0d.png

下图是一个恶性样本:
112502vf44f8b8w4c7e72z.png

实验过程

   由于能力和精力有限,我并没有选用CNN模型作为分类器。一方面是因为不借助PyTorch框架实现CNN对我来说过于困难,另一方面是因为本次课内实验提供的数据量太小,我觉得没有必要通过卷积神经网络来进行分类,故本次实验选用了相对简单的全连接神经网络实现。
  ①数据集的读取

本次实验的数据集正类样本和负类样本存放于两个不同的目录,因此可以调用Python的os模块,列举出目录中的所有文件并进行读取,作为数据集的特征。经过观察各图片的命名,可以发现所有的正类样本命名均为SOB_B_...,负类样本命名均为SOB_M_...,故可以从文件名入手,以下划线作为分隔符对文件名运用split方法,通过索引为1的元素(B或M)来确定数据样本的标签。每读取一个样本,就将其加入dataset列表,便于后续划分训练集和验证集。
  1. def read_data(self):
  2.     dataset = []
  3.     for dir_name in self.dir_names:
  4.         for filename in os.listdir(r'./data/' + dir_name):
  5.             img = cv2.imread(r'./data/' + dir_name + '/' + filename)
  6.             label = 0 if filename.split('_')[1] == 'B' else 1
  7.             dataset.append([img, label])
  8.     return dataset
复制代码
②数据集的划分

根据设定的test_size参数来划分出训练集和验证集。划分前通过random.shuffle方法对数据集洗牌,确定训练集和测试集大小后通过切片的方式进行划分,进一步提取出对应的特征和标签。对于数据样本的特征,输入网络时可将图像三个通道的像素展平,但这样可能会丢失一些上下相邻像素点之间的关联
  1. def train_test_split(self, dataset):
  2.     random.shuffle(dataset)
  3.     data_len = len(dataset)
  4.     test_len = int(data_len * self.test_size)
  5.     train_len = data_len - test_len
  6.     train, test = dataset[:train_len], dataset[train_len:]
  7.     X_train = np.array([i[0] for i in train])
  8.     y_train = np.array([[i[1]] for i in train]).reshape(1, -1)
  9.     X_test = np.array([i[0] for i in test])
  10.     y_test = np.array([[i[1]] for i in test]).reshape(1, -1)
  11.     X_train = X_train.reshape((X_train.shape[0], -1)).T
  12.     X_test = X_test.reshape((X_test.shape[0], -1)).T
  13.     return X_train, X_test, y_train, y_test
复制代码
③全连接神经网络

编写my_nn类实现全连接神经网络,从本质上来说是通过训练得到每两层之间的权重和偏置(即W1、b1、W2、b2),训练之前应对其进行随机初始化。神经网络的训练过程可以分为前向传播反向传播。在前向传播中,设置隐藏层后使用tanh函数激活,输出结果使用sigmoid函数(调用scipy.special.expit实现)映射到                               [                      0                      ,                      1                      ]                          [0, 1]               [0,1] 区间。反向传播则是对矩阵求导,通过梯度下降法优化网络参数。
  1. def init_parameters(self):
  2.     W1 = np.random.randn(self.hidden_layer, self.input_layer) * 0.01
  3.     b1 = np.zeros((self.hidden_layer, 1))
  4.     W2 = np.random.randn(self.output_layer, self.hidden_layer) * 0.01
  5.     b2 = np.zeros((self.output_layer, 1))
  6.     return W1, W2, b1, b2
  7. def tanh(self, x):
  8.     return 2 / (1 + np.exp(-2 * x)) - 1
  9. def forward(self, W1, W2, b1, b2, x):
  10.     z1 = np.dot(W1, x) + b1
  11.     a1 = self.tanh(z1)
  12.     z2 = np.dot(W2, a1) + b2
  13.     y_hat = expit(z2)
  14.     return y_hat, a1
  15. def backward(self, y_hat, a1, W2, N):
  16.     dz2 = (-1 / N) * (self.y * (1 - y_hat) - y_hat * (1 - self.y))
  17.     dW2 = np.dot(dz2, a1.T)
  18.     db2 = dz2.sum(axis=1, keepdims=True)
  19.     dz1 = np.dot(W2.T, dz2) * (1 - a1 ** 2)
  20.     dW1 = np.dot(dz1, self.X.T)
  21.     db1 = dz1.sum(axis=1, keepdims=True)
  22.     return {'dW2': dW2, 'db2': db2, 'dW1': dW1, 'db1': db1}
  23. def optim_GD(self, W1, W2, b1, b2, lr, grids):
  24.     W1 -= lr * grids['dW1']
  25.     W2 -= lr * grids['dW2']
  26.     b1 -= lr * grids['db1']
  27.     b2 -= lr * grids['db2']
  28.     return W1, W2, b1, b2
复制代码
本次实验的神经网络设置隐藏层神经元数量为16,训练迭代次数为2000,学习率为0.1,损失函数选择交叉熵损失函数。
完整代码实现

项目结构如下图所示:
112503p3jloc66lb2fj8tz.png

my_nn.py
  1. import os
  2. import random
  3. import cv2
  4. import numpy as np
  5. from scipy.special import expit
  6. class my_nn:
  7.     def __init__(self, X, y, hidden_layer, iters, lr):
  8.         self.X = X
  9.         self.y = y
  10.         self.input_layer = self.X.shape[0]
  11.         self.output_layer = self.y.shape[0]
  12.         self.hidden_layer = hidden_layer
  13.         self.iters = iters
  14.         self.lr = lr
  15.     def init_parameters(self):
  16.         W1 = np.random.randn(self.hidden_layer, self.input_layer) * 0.01
  17.         b1 = np.zeros((self.hidden_layer, 1))
  18.         W2 = np.random.randn(self.output_layer, self.hidden_layer) * 0.01
  19.         b2 = np.zeros((self.output_layer, 1))
  20.         return W1, W2, b1, b2
  21.     def tanh(self, x):
  22.         return 2 / (1 + np.exp(-2 * x)) - 1
  23.     def forward(self, W1, W2, b1, b2, x):
  24.         z1 = np.dot(W1, x) + b1
  25.         a1 = self.tanh(z1)
  26.         z2 = np.dot(W2, a1) + b2
  27.         y_hat = expit(z2)
  28.         return y_hat, a1
  29.     def backward(self, y_hat, a1, W2, N):
  30.         dz2 = (-1 / N) * (self.y * (1 - y_hat) - y_hat * (1 - self.y))
  31.         dW2 = np.dot(dz2, a1.T)
  32.         db2 = dz2.sum(axis=1, keepdims=True)
  33.         dz1 = np.dot(W2.T, dz2) * (1 - a1 ** 2)
  34.         dW1 = np.dot(dz1, self.X.T)
  35.         db1 = dz1.sum(axis=1, keepdims=True)
  36.         return {'dW2': dW2, 'db2': db2, 'dW1': dW1, 'db1': db1}
  37.     def optim_GD(self, W1, W2, b1, b2, lr, grids):
  38.         W1 -= lr * grids['dW1']
  39.         W2 -= lr * grids['dW2']
  40.         b1 -= lr * grids['db1']
  41.         b2 -= lr * grids['db2']
  42.         return W1, W2, b1, b2
  43.     def cross_entropy(self, y_hat, N):
  44.         assert self.y.shape == y_hat.shape
  45.         return (-1 / N) * (np.dot(self.y, np.log(y_hat.T)) + np.dot((1 - self.y), np.log((1 - y_hat).T)))
  46.     def cal_acc(self, y, y_hat):
  47.         y_hat = y_hat.T
  48.         for i in range(len(y_hat)):
  49.             y_hat[i] = 0 if y_hat[i] < 0.5 else 1
  50.         acc = (np.dot(y, y_hat) + np.dot(1 - y, 1 - y_hat)) / float(len(y_hat))
  51.         return acc, y_hat
  52.     def fit(self):
  53.         W1, W2, b1, b2 = self.init_parameters()
  54.         print('-' * 40 + ' Train ' + '-' * 40)
  55.         for i in range(1, self.iters + 1):
  56.             y_hat, a1 = self.forward(W1, W2, b1, b2, self.X)
  57.             if i % 50 == 0:
  58.                 cost = self.cross_entropy(y_hat, self.X.shape[1])[0][0]
  59.                 acc, _ = self.cal_acc(self.y, y_hat)
  60.                 acc = acc[0][0]
  61.                 print('[Train] Iter: {:<4d} | Loss: {:<6.4f} | Accuracy: {:<6.4f}'.format(i, cost, acc))
  62.             grids = self.backward(y_hat, a1, W2, self.X.shape[1])
  63.             W1, W2, b1, b2 = self.optim_GD(W1, W2, b1, b2, self.lr, grids)
  64.         return {'W1': W1, 'W2': W2, 'b1': b1, 'b2': b2}
  65.     def eval(self, X_test, y_test, parameters):
  66.         W1 = parameters['W1']
  67.         W2 = parameters['W2']
  68.         b1 = parameters['b1']
  69.         b2 = parameters['b2']
  70.         y_pred, _ = self.forward(W1, W2, b1, b2, X_test)
  71.         acc, y_pred = self.cal_acc(y_test, y_pred)
  72.         acc = acc[0][0]
  73.         print('-' * 40 + ' Eval ' + '-' * 40)
  74.         print('[Eval] Accuracy: {:<6.4f}'.format(acc))
  75.         return y_pred
  76. class Dataset:
  77.     def __init__(self, dir_names, test_size):
  78.         self.dir_names = dir_names
  79.         self.test_size = test_size
  80.     def read_data(self):
  81.         dataset = []
  82.         for dir_name in self.dir_names:
  83.             for filename in os.listdir(r'./data/' + dir_name):
  84.                 img = cv2.imread(r'./data/' + dir_name + '/' + filename)
  85.                 label = 0 if filename.split('_')[1] == 'B' else 1
  86.                 dataset.append([img, label])
  87.         return dataset
  88.     def train_test_split(self, dataset):
  89.         random.shuffle(dataset)
  90.         data_len = len(dataset)
  91.         test_len = int(data_len * self.test_size)
  92.         train_len = data_len - test_len
  93.         train, test = dataset[:train_len], dataset[train_len:]
  94.         X_train = np.array([i[0] for i in train])
  95.         y_train = np.array([[i[1]] for i in train]).reshape(1, -1)
  96.         X_test = np.array([i[0] for i in test])
  97.         y_test = np.array([[i[1]] for i in test]).reshape(1, -1)
  98.         X_train = X_train.reshape((X_train.shape[0], -1)).T
  99.         X_test = X_test.reshape((X_test.shape[0], -1)).T
  100.         return X_train, X_test, y_train, y_test
复制代码
main.py
  1. from my_nn import Dataset, my_nn
  2. dir_names = ['benign', 'malignant']
  3. dataset = Dataset(dir_names, 0.3)
  4. X_train, X_test, y_train, y_test = dataset.train_test_split(dataset.read_data())
  5. model = my_nn(X_train, y_train, 16, 2000, 0.1)
  6. parameters = model.fit()
  7. model.eval(X_test, y_test, parameters)
复制代码
实验结果如下图所示:
112503oopdj111kpvn7o8o.png

由于只是一次课内实验,数据样本过少(正、负类样本各10张),很难通过分类的准确率来评判这个模型,但训练过程中损失值是在不断下降的。

来源:https://blog.csdn.net/qq_45554010/article/details/124620862
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
回复

使用道具 举报

提醒:禁止复制他人回复等『恶意灌水』行为,违者重罚!
您需要登录后才可以回帖 登录 | 注册[Register] 手机动态码快速登录 微信登录

本版积分规则

发布主题 快速回复 收藏帖子 返回列表 客服中心 搜索
简体中文 繁體中文 English 한국 사람 日本語 Deutsch русский بالعربية TÜRKÇE português คนไทย french

QQ|RSS订阅|小黑屋|处罚记录|手机版|联系我们|Archiver|医工互联 |粤ICP备2021178090号 |网站地图

GMT+8, 2025-2-23 11:44 , Processed in 0.278650 second(s), 67 queries .

Powered by Discuz!

Copyright © 2001-2023, Discuz! Team.

快速回复 返回顶部 返回列表