{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"pygments_lexer":"ipython3","nbconvert_exporter":"python","version":"3.6.4","file_extension":".py","codemirror_mode":{"name":"ipython","version":3},"name":"python","mimetype":"text/x-python"}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"%time\n# This Python 3 environment comes with many helpful analytics libraries installed\n# It is defined by the kaggle/python docker image: https://github.com/kaggle/docker-python\n# For example, here's several helpful packages to load in \n\nimport numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\n\n# Input data files are available in the \"../input/\" directory.\n# For example, running this (by clicking run or pressing Shift+Enter) will list the files in the input directory\n\n# 返回指定路径下的文件和文件夹列表\nimport os\nprint(os.listdir(\"../input\"))\n\n# Any results you write to the current directory are saved as output.","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","execution":{"iopub.status.busy":"2022-06-01T07:15:33.612402Z","iopub.execute_input":"2022-06-01T07:15:33.612799Z","iopub.status.idle":"2022-06-01T07:15:33.623639Z","shell.execute_reply.started":"2022-06-01T07:15:33.612740Z","shell.execute_reply":"2022-06-01T07:15:33.622615Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# 导入后面要使用的包/库","metadata":{}},{"cell_type":"code","source":"# !python      #查看python 版本","metadata":{"execution":{"iopub.status.busy":"2022-06-01T07:15:33.628752Z","iopub.execute_input":"2022-06-01T07:15:33.629192Z","iopub.status.idle":"2022-06-01T07:15:33.635277Z","shell.execute_reply.started":"2022-06-01T07:15:33.629002Z","shell.execute_reply":"2022-06-01T07:15:33.634114Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# !python      #查看python 版本\n# !nvcc --version   #查看cuda版本\n","metadata":{"execution":{"iopub.status.busy":"2022-06-01T07:15:33.637447Z","iopub.execute_input":"2022-06-01T07:15:33.637987Z","iopub.status.idle":"2022-06-01T07:15:33.645645Z","shell.execute_reply.started":"2022-06-01T07:15:33.637933Z","shell.execute_reply":"2022-06-01T07:15:33.644707Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":" import tensorflow as tf\ntf.__version__","metadata":{"execution":{"iopub.status.busy":"2022-06-01T07:15:33.647190Z","iopub.execute_input":"2022-06-01T07:15:33.647901Z","iopub.status.idle":"2022-06-01T07:15:34.539207Z","shell.execute_reply.started":"2022-06-01T07:15:33.647841Z","shell.execute_reply":"2022-06-01T07:15:34.538298Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"!nvidia-smi","metadata":{"execution":{"iopub.status.busy":"2022-06-01T07:15:34.542401Z","iopub.execute_input":"2022-06-01T07:15:34.542665Z","iopub.status.idle":"2022-06-01T07:15:35.196366Z","shell.execute_reply.started":"2022-06-01T07:15:34.542621Z","shell.execute_reply":"2022-06-01T07:15:35.195475Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import matplotlib.pyplot as plt\n# io.imread()是图片读取    io.imshow()是图片显示\nimport skimage.io\n# from skimage import io,data\n# img = io.imread('../input/resized-2015-2019-blindness-detection-images/resized train 15/10029_left.jpg')\n# io.imshow(img)\n# print ('img的宽度：', img.shape[0])  #图片宽度\n# print ('img的高度：', img.shape[1])  #图片高度\nfrom skimage.transform import resize\n# imgaug是一个数据增强的库\nfrom imgaug import augmenters as iaa\n# tqdm是一个python进度条\nfrom tqdm import tqdm\n# import PIL用于导入可以产生各种处理图片的函数\nimport PIL\nfrom PIL import Image, ImageOps\n# opencv库是计算机视觉库，调用的时候经常import cv2\nimport cv2\n# sklearn.utils.shuffle()用来打乱样本\n# sklearn.utils.class_weight对训练集里的每个类别加一个权重。类别的样本数多，它的权重就低，反之则权重就高\nfrom sklearn.utils import class_weight, shuffle\n# tf.keras.losses实例是用来计算真实标签（ y_true ）和预测标签之间（ y_pred ）的loss损失\nfrom keras.losses import binary_crossentropy, categorical_crossentropy\n# 导入网络模型：已经训练好权重的resnet50      preprocess_input是为resnet网络准备新的图像输入\n#                                         用法：image = preprocess_input(image)\nfrom keras.applications.resnet50 import preprocess_input\n# 导入backend模块\nimport keras.backend as K\nimport tensorflow as tf\n# 导入各种评估指标函数\nfrom sklearn.metrics import f1_score, fbeta_score, cohen_kappa_score, accuracy_score\n# 导入Sequence类，用keras.utils.Sequence()构建一个数据生成器\nfrom keras.utils import Sequence \n# keras.utils.to_categorical将类别向量转换成只用二进制（0和1）表示的形式\nfrom keras.utils import to_categorical\n# 调用import train_test_split()函数来划分训练集和测试集\nfrom sklearn.model_selection import train_test_split\n\nWORKERS = 2\nCHANNEL = 3\n\nimport warnings\nwarnings.filterwarnings(\"ignore\")  #“ignore是忽略警告”有时候代码能运行出来但是会有警告\nSIZE = 300\nNUM_CLASSES = 5","metadata":{"_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","execution":{"iopub.status.busy":"2022-06-01T07:15:35.200653Z","iopub.execute_input":"2022-06-01T07:15:35.200912Z","iopub.status.idle":"2022-06-01T07:15:36.901310Z","shell.execute_reply.started":"2022-06-01T07:15:35.200865Z","shell.execute_reply":"2022-06-01T07:15:36.900469Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# pd.read_csv()接口用于读取csv格式数据文件\ndf_train = pd.read_csv('../input/resized-2015-2019-diabetic-retinopathy-detection/labels/trainLabels15.csv')\ndf_test = pd.read_csv('../input/resized-2015-2019-diabetic-retinopathy-detection/labels/testLabels15.csv')\n\nx = df_train['image']\ny = df_train['level']\n\n# x， y= shuffle(x, y, random_state=1):把x和y一一对应后打乱，如果想要每次打乱后的排序都相同，就给\n# random_state设置实值\nx, y = shuffle(x, y, random_state=8)\ny.hist()   #构建直方图\n\n","metadata":{"execution":{"iopub.status.busy":"2022-06-01T07:15:36.903036Z","iopub.execute_input":"2022-06-01T07:15:36.903627Z","iopub.status.idle":"2022-06-01T07:15:37.520412Z","shell.execute_reply.started":"2022-06-01T07:15:36.903572Z","shell.execute_reply":"2022-06-01T07:15:37.519378Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# 划分数据集","metadata":{}},{"cell_type":"code","source":"# # to_categorical()中y是待转换的标签数组，num_classes是标签类别数，注意类别个数加一\n# # https://blog.csdn.net/weixin_42886817/article/details/100118621\ny = to_categorical(y, num_classes=NUM_CLASSES)  #num_classes可以设置为具体数值，但最好这样设置\n\n# # train_x，train_y是原始数据集划分出来作为训练模型的\n# # valid_x, valid_y用于评价训练出来的模型好坏，score评分的时候用\n# # x:所要划分的样本特征集      y:所要划分的样本结果\n# # test_size:“测试集”比例\n# # stratify=y是为了保持和y split前 类的分布,通常在这种类分布不平衡的情况下会用到stratify\n# # 即划分后的训练集和测试集里边的类别比例都保持和划分前的比例一样\n# # random_state是0或者None的时候，每次划分的结果不一样，是具体数值的时候每次划分都一样\n# train_x, test_x, train_y, test_y = train_test_split(x, y, test_size=0.2,\n#                                                       stratify=y, random_state=8)\n# train_x, valid_x, train_y, valid_y = train_test_split(x, y, test_size=0.2,\n#                                                       stratify=y, random_state=8)\n# print(train_x.shape)\n# print(train_y.shape)\n# print(valid_x.shape)\n# print(valid_y.shape)\n# print(test_x.shape)\n# print(test_y.shape)\n# train_x，train_y是原始数据集划分出来作为训练模型的\n# valid_x, valid_y用于评价训练出来的模型好坏，score评分的时候用\n# x:所要划分的样本特征集      y:所要划分的样本结果\n# test_size:“测试集”比例\n# stratify=y是为了保持和y split前 类的分布,通常在这种类分布不平衡的情况下会用到stratify\n# 即划分后的训练集和测试集里边的类别比例都保持和划分前的比例一样\n# random_state是0或者None的时候，每次划分的结果不一样，是具体数值的时候每次划分都一样\ntrain_x, valid_x, train_y, valid_y = train_test_split(x, y, test_size=0.15,\n                                                      stratify=y, random_state=8)\nprint(train_x.shape)\nprint(train_y.shape)\nprint(valid_x.shape)\nprint(valid_y.shape)","metadata":{"execution":{"iopub.status.busy":"2022-06-01T07:15:37.525518Z","iopub.execute_input":"2022-06-01T07:15:37.529071Z","iopub.status.idle":"2022-06-01T07:15:37.987399Z","shell.execute_reply.started":"2022-06-01T07:15:37.529008Z","shell.execute_reply":"2022-06-01T07:15:37.985480Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# 数据增强","metadata":{}},{"cell_type":"code","source":"# https://github.com/aleju/imgaug\n# iaa.Sometimes(0.5)有50%的图片会被应用一部分Augmenters，剩下的50%会被应用另外的Augmenters\nsometimes = lambda aug: iaa.Sometimes(0.5, aug)\n# 产生一个做数据增强的Sequential实例\nseq = iaa.Sequential([\n    sometimes(\n#         iaa.OneOf()每次从一系列Augmenters中选择一个来变换\n        iaa.OneOf([\n#             iaa.Add(，per_channel=True)每个通道使用相同的 value,这里是在（-10~10）之间随机抽取一个数，\n#             并保持之后都是这个数\n#             iaa.Add(,per_channel=False)每个通道使用不同的 value，每抽取一次，这个值都会发生变化\n#             iaa.Add(,per_channel=0.5)在50%的图像中，所有通道的值相同（这个值是所加的随机值）\n#             在其他50%的图像中，每个通道的值不同，但是都会在-10~10之间\n#             per_channel：每个通道是否使用相同的 value\n         \n            iaa.Add((-10, 10), per_channel=0.5),\n#            iaa.Multiply() 给图像中的每个像素点乘一个值使得图片更亮或者更暗，这里同样是操作50%的图像\n            iaa.Multiply((0.9, 1.1), per_channel=0.5),\n#             将对比度标准化为0.9至1.1倍，对50%的图像，另外50%不变\n#             可以把这里的对比度理解为一张图片色彩差异，差异越大，图像越清晰醒目，色彩也越鲜明艳丽\n            iaa.ContrastNormalization((0.9, 1.1), per_channel=0.5)\n        ])\n    ),\n    iaa.Fliplr(0.5),  #对50%的图像进行镜像翻转\n    iaa.Crop(percent=(0, 0.1)),  #对图像的四个方向即四条边都裁剪0-10%，可以填充常数也可以填充随机值\n    iaa.Flipud(0.5)   #对50%的图像进行左右翻转\n],random_order=True)\n","metadata":{"execution":{"iopub.status.busy":"2022-06-01T07:15:37.988937Z","iopub.execute_input":"2022-06-01T07:15:37.989219Z","iopub.status.idle":"2022-06-01T07:15:38.001288Z","shell.execute_reply.started":"2022-06-01T07:15:37.989172Z","shell.execute_reply":"2022-06-01T07:15:38.000387Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# 优化","metadata":{}},{"cell_type":"code","source":"# 在自定义优化器时，一般都会用到这三行代码\nfrom keras.legacy import interfaces\nfrom keras.optimizers import Optimizer\nfrom keras import backend as K    #使用抽象keras backend API来写代码\n\n\nclass AdamAccumulate_v1(Optimizer):\n    def __init__(self, lr=0.001, beta_1=0.9, beta_2=0.999,\n                 epsilon=None, decay=0., amsgrad=False, accum_iters=20, **kwargs):\n        super(AdamAccumulate, self).__init__(**kwargs)\n        with K.name_scope(self.__class__.__name__):\n            self.iterations = K.variable(0, dtype='int64', name='iterations')\n            self.effective_iterations = K.variable(0, dtype='int64', name='effective_iterations')\n\n            self.lr = K.variable(lr, name='lr')\n            self.beta_1 = K.variable(beta_1, name='beta_1')\n            self.beta_2 = K.variable(beta_2, name='beta_2')\n            self.decay = K.variable(decay, name='decay')\n        if epsilon is None:\n            epsilon = K.epsilon()\n        self.epsilon = epsilon\n        self.initial_decay = decay\n        self.amsgrad = amsgrad\n        self.accum_iters = K.variable(accum_iters, dtype='int64')\n\n    @interfaces.legacy_get_updates_support\n    def get_updates(self, loss, params):\n        grads = self.get_gradients(loss, params)\n\n        self.updates = [K.update(self.iterations, self.iterations + 1)]\n\n        flag = K.equal(self.iterations % self.accum_iters, self.accum_iters - 1)\n        flag = K.cast(flag, K.floatx())\n\n        self.updates.append(K.update(self.effective_iterations,\n                                     self.effective_iterations + K.cast(flag, 'int64')))\n\n        lr = self.lr\n        if self.initial_decay > 0:\n            lr = lr * (1. / (1. + self.decay * K.cast(self.effective_iterations,\n                                                      K.dtype(self.decay))))\n\n        t = K.cast(self.effective_iterations, K.floatx()) + 1\n\n        lr_t = lr * (K.sqrt(1. - K.pow(self.beta_2, t)) /\n                     (1. - K.pow(self.beta_1, t)))\n\n        ms = [K.zeros(K.int_shape(p), dtype=K.dtype(p)) for p in params]\n        vs = [K.zeros(K.int_shape(p), dtype=K.dtype(p)) for p in params]\n        gs = [K.zeros(K.int_shape(p), dtype=K.dtype(p)) for p in params]\n\n        if self.amsgrad:\n            vhats = [K.zeros(K.int_shape(p), dtype=K.dtype(p)) for p in params]\n        else:\n            vhats = [K.zeros(1) for _ in params]\n        self.weights = [self.iterations] + ms + vs + vhats\n\n        for p, g, m, v, vhat, gg in zip(params, grads, ms, vs, vhats, gs):\n\n            gg_t = (1 - flag) * (gg + g)\n            m_t = (self.beta_1 * m) + (1. - self.beta_1) * (gg + flag * g) / K.cast(self.accum_iters, K.floatx())\n            v_t = (self.beta_2 * v) + (1. - self.beta_2) * K.square(\n                (gg + flag * g) / K.cast(self.accum_iters, K.floatx()))\n\n            if self.amsgrad:\n                vhat_t = K.maximum(vhat, v_t)\n                p_t = p - flag * lr_t * m_t / (K.sqrt(vhat_t) + self.epsilon)\n                self.updates.append(K.update(vhat, vhat_t))\n            else:\n                p_t = p - flag * lr_t * m_t / (K.sqrt(v_t) + self.epsilon)\n\n            self.updates.append((m, flag * m_t + (1 - flag) * m))\n            self.updates.append((v, flag * v_t + (1 - flag) * v))\n            self.updates.append((gg, gg_t))\n            new_p = p_t\n\n            # Apply constraints.\n            if getattr(p, 'constraint', None) is not None:\n                new_p = p.constraint(new_p)\n\n            self.updates.append(K.update(p, new_p))\n        return self.updates\n\n    def get_config(self):\n        config = {'lr': float(K.get_value(self.lr)),\n                  'beta_1': float(K.get_value(self.beta_1)),\n                  'beta_2': float(K.get_value(self.beta_2)),\n                  'decay': float(K.get_value(self.decay)),\n                  'epsilon': self.epsilon,\n                  'amsgrad': self.amsgrad}\n        base_config = super(AdamAccumulate, self).get_config()\n        return dict(list(base_config.items()) + list(config.items()))\n\n\nclass AdamAccumulate(Optimizer):   #优化器设置\n    def __init__(self, lr=0.001, beta_1=0.9, beta_2=0.999,\n                 epsilon=None, decay=0., amsgrad=False, accum_iters=2, **kwargs):\n        if accum_iters < 1:\n            raise ValueError('accum_iters must be >= 1')\n        super(AdamAccumulate, self).__init__(**kwargs)\n        with K.name_scope(self.__class__.__name__):\n            self.iterations = K.variable(0, dtype='int64', name='iterations')\n            self.lr = K.variable(lr, name='lr')\n            self.beta_1 = K.variable(beta_1, name='beta_1')\n            self.beta_2 = K.variable(beta_2, name='beta_2')\n            self.decay = K.variable(decay, name='decay')\n        if epsilon is None:\n            epsilon = K.epsilon()\n        self.epsilon = epsilon\n        self.initial_decay = decay\n        self.amsgrad = amsgrad\n        self.accum_iters = K.variable(accum_iters, K.dtype(self.iterations))\n        self.accum_iters_float = K.cast(self.accum_iters, K.floatx())\n\n    @interfaces.legacy_get_updates_support\n    def get_updates(self, loss, params):\n        grads = self.get_gradients(loss, params)\n        self.updates = [K.update_add(self.iterations, 1)]\n\n        lr = self.lr\n\n        completed_updates = K.cast(K.tf.floor(self.iterations / self.accum_iters), K.floatx())\n\n        if self.initial_decay > 0:\n            lr = lr * (1. / (1. + self.decay * completed_updates))\n\n        t = completed_updates + 1\n\n        lr_t = lr * (K.sqrt(1. - K.pow(self.beta_2, t)) / (1. - K.pow(self.beta_1, t)))\n\n        # self.iterations incremented after processing a batch\n        # batch:              1 2 3 4 5 6 7 8 9\n        # self.iterations:    0 1 2 3 4 5 6 7 8\n        # update_switch = 1:        x       x    (if accum_iters=4)\n        update_switch = K.equal((self.iterations + 1) % self.accum_iters, 0)\n        update_switch = K.cast(update_switch, K.floatx())\n\n        ms = [K.zeros(K.int_shape(p), dtype=K.dtype(p)) for p in params]\n        vs = [K.zeros(K.int_shape(p), dtype=K.dtype(p)) for p in params]\n        gs = [K.zeros(K.int_shape(p), dtype=K.dtype(p)) for p in params]\n\n        if self.amsgrad:\n            vhats = [K.zeros(K.int_shape(p), dtype=K.dtype(p)) for p in params]\n        else:\n            vhats = [K.zeros(1) for _ in params]\n\n        self.weights = [self.iterations] + ms + vs + vhats\n\n        for p, g, m, v, vhat, tg in zip(params, grads, ms, vs, vhats, gs):\n\n            sum_grad = tg + g\n            avg_grad = sum_grad / self.accum_iters_float\n\n            m_t = (self.beta_1 * m) + (1. - self.beta_1) * avg_grad\n            v_t = (self.beta_2 * v) + (1. - self.beta_2) * K.square(avg_grad)\n\n            if self.amsgrad:\n                vhat_t = K.maximum(vhat, v_t)\n                p_t = p - lr_t * m_t / (K.sqrt(vhat_t) + self.epsilon)\n                self.updates.append(K.update(vhat, (1 - update_switch) * vhat + update_switch * vhat_t))\n            else:\n                p_t = p - lr_t * m_t / (K.sqrt(v_t) + self.epsilon)\n\n            self.updates.append(K.update(m, (1 - update_switch) * m + update_switch * m_t))\n            self.updates.append(K.update(v, (1 - update_switch) * v + update_switch * v_t))\n            self.updates.append(K.update(tg, (1 - update_switch) * sum_grad))\n            new_p = p_t\n\n            # Apply constraints.\n            if getattr(p, 'constraint', None) is not None:\n                new_p = p.constraint(new_p)\n\n            self.updates.append(K.update(p, (1 - update_switch) * p + update_switch * new_p))\n        return self.updates\n\n    def get_config(self):\n        config = {'lr': float(K.get_value(self.lr)),\n                  'beta_1': float(K.get_value(self.beta_1)),\n                  'beta_2': float(K.get_value(self.beta_2)),\n                  'decay': float(K.get_value(self.decay)),\n                  'epsilon': self.epsilon,\n                  'amsgrad': self.amsgrad}\n        base_config = super(AdamAccumulate, self).get_config()\n        return dict(list(base_config.items()) + list(config.items()))","metadata":{"execution":{"iopub.status.busy":"2022-06-01T07:15:38.003052Z","iopub.execute_input":"2022-06-01T07:15:38.003740Z","iopub.status.idle":"2022-06-01T07:15:38.064191Z","shell.execute_reply.started":"2022-06-01T07:15:38.003472Z","shell.execute_reply":"2022-06-01T07:15:38.063346Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# 把数据喂进网络","metadata":{}},{"cell_type":"code","source":"# Sequence类必须重载三个私有方法，__init__、__len__和__getitem__\n# __init__初始化数据，让样本数据集通过形参顺利传进来\n# __len__用于计算样本数据长度，也就是跑完一遍数据要运行模型多少次\n# __getitem__生成批量数据，喂给神经网络模型训练\nclass My_Generator(Sequence):\n\n    def __init__(self, image_filenames, labels,\n                 batch_size, is_train=False,  #is_train=False记录每一个Batch的均值和方差, 训练完成之后按照下式计算整体的均值和方差\n                 mix=False, augment=False):    #是否混合，是否使用随机增强的样本\n        self.image_filenames, self.labels = image_filenames, labels\n        self.batch_size = batch_size\n        self.is_train = is_train\n        self.is_augment = augment\n        if(self.is_train):\n            self.on_epoch_end()  #如果记录，那么就在每个epoch之后进行\n        self.is_mix = mix\n\n    def __len__(self):   #相当于计算batch：总长度/batch_size\n        return int(np.ceil(len(self.image_filenames) / float(self.batch_size)))\n\n    def __getitem__(self, idx):\n        batch_x = self.image_filenames[idx * self.batch_size:(idx + 1) * self.batch_size]\n        batch_y = self.labels[idx * self.batch_size:(idx + 1) * self.batch_size]\n\n        if(self.is_train):\n            return self.train_generate(batch_x, batch_y)\n        return self.valid_generate(batch_x, batch_y)\n    \n# 跑完一个epoch后，想要实现什么，可以通过这个函数实现\n# 比如这里，跑完一个epoch后对image和label进行一次清洗\n    def on_epoch_end(self):\n        if(self.is_train):\n            self.image_filenames, self.labels = shuffle(self.image_filenames, self.labels)\n    \n    def mix_up(self, x, y):   #混合增强，x是样本，y是标签\n        lam = np.random.beta(0.2, 0.4)  #认为lam是混合系数就可以，而混合系数就是通过0.2和0.4计算出的，有公式\n        ori_index = np.arange(int(len(x)))  #认为ori_index和index_array分别是两个样本及其标签的表示\n        index_array = np.arange(int(len(x)))\n        np.random.shuffle(index_array)        \n        \n#         mixed_x = 系数 * ori_index +（1-系数） * index_array\n#         mixed_x是混合后的样本，mixed_y是混合后的样本标签\n        mixed_x = lam * x[ori_index] + (1 - lam) * x[index_array]\n        mixed_y = lam * y[ori_index] + (1 - lam) * y[index_array]\n        \n        return mixed_x, mixed_y\n\n    def train_generate(self, batch_x, batch_y):  #batch_x和batch_y是一个一个的batch\n        batch_images = []  #创建一个空list列表\n        for (sample, label) in zip(batch_x, batch_y):\n            img = cv2.imread('../input/resized-2015-2019-diabetic-retinopathy-detection/resized_traintest15_train19/'+sample+'.jpg')\n#             print(img.shape)\n            img = cv2.resize(img, (SIZE, SIZE),interpolation=cv2.INTER_AREA)  #对图像大小进行缩放（size， size）分别是水平和竖直方向\n            if(self.is_augment):\n                img = seq.augment_image(img)\n            batch_images.append(img)  #append() 方法将img添加到现有列表中\n        batch_images = np.array(batch_images, np.float32) / 255\n        batch_y = np.array(batch_y, np.float32)\n        if(self.is_mix):\n            batch_images, batch_y = self.mix_up(batch_images, batch_y)\n        return batch_images, batch_y\n   \n\n    def valid_generate(self, batch_x, batch_y):\n        batch_images = []\n        for (sample, label) in zip(batch_x, batch_y):\n            img = cv2.imread('../input/resized-2015-2019-diabetic-retinopathy-detection/resized_traintest15_train19/'+sample+'.jpg')\n                \n            img = cv2.resize(img, (SIZE, SIZE),interpolation=cv2.INTER_AREA)\n                \n                \n            batch_images.append(img)\n        batch_images = np.array(batch_images, np.float32) / 255\n        batch_y = np.array(batch_y, np.float32)\n        return batch_images, batch_y\n","metadata":{"execution":{"iopub.status.busy":"2022-06-01T07:15:38.065771Z","iopub.execute_input":"2022-06-01T07:15:38.066142Z","iopub.status.idle":"2022-06-01T07:15:38.088634Z","shell.execute_reply.started":"2022-06-01T07:15:38.066089Z","shell.execute_reply":"2022-06-01T07:15:38.087558Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# ImageDataGenerator类：实时数据增强生成批量图像数据向量。训练时会无限循环生成数据，直到达到规定的epoch次数为止\nfrom keras.preprocessing.image import ImageDataGenerator\nfrom keras.models import Sequential, load_model    #load_model是加载模型\nfrom keras.layers import (Activation, Dropout, Flatten, Dense, GlobalMaxPooling2D,\n                          BatchNormalization, Input, Conv2D, GlobalAveragePooling2D)\nfrom keras.applications.resnet50 import ResNet50   #读取resnet50中的ResNet50\n# 回调函数的使用https://keras.io/zh/callbacks/\nfrom keras.callbacks import ModelCheckpoint\nfrom keras import metrics\nfrom keras.optimizers import Adam \nfrom keras import backend as K\nimport keras\nfrom keras.models import Model","metadata":{"execution":{"iopub.status.busy":"2022-06-01T07:15:38.090073Z","iopub.execute_input":"2022-06-01T07:15:38.090562Z","iopub.status.idle":"2022-06-01T07:15:38.101373Z","shell.execute_reply.started":"2022-06-01T07:15:38.090510Z","shell.execute_reply":"2022-06-01T07:15:38.100753Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# 网络模型","metadata":{}},{"cell_type":"code","source":"function = \"softmax\"\n# 创建一个模型\ndef create_model(input_shape, n_out):\n    input_tensor = Input(shape=input_shape)\n    base_model = ResNet50(include_top=False,  #include_top：是否保留顶层的3个全连接网络\n                   weights=None,  #参数随机初始化，不加载预训练模型\n                   input_tensor=input_tensor)\n    base_model.load_weights('../input/resnet50/resnet50_weights_tf_dim_ordering_tf_kernels_notop.h5')\n    x = GlobalAveragePooling2D()(base_model.output)\n    x = Dropout(0.5)(x)\n    x = Dense(1024, activation='relu')(x)\n    x = Dropout(0.5)(x)\n    final_output = Dense(n_out, activation=function, name='final_output')(x)\n    model = Model(input_tensor, final_output)\n    \n    return model","metadata":{"execution":{"iopub.status.busy":"2022-06-01T07:15:38.103055Z","iopub.execute_input":"2022-06-01T07:15:38.103317Z","iopub.status.idle":"2022-06-01T07:15:38.115941Z","shell.execute_reply.started":"2022-06-01T07:15:38.103274Z","shell.execute_reply":"2022-06-01T07:15:38.115131Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# 写一个LossHistory类，保存loss和acc","metadata":{}},{"cell_type":"code","source":"# class LossHistory(keras.callbacks.Callback):\n#     def on_train_begin(self, logs={}):\n#         self.losses = {'batch':[], 'epoch':[]}\n#         self.accuracy = {'batch':[], 'epoch':[]}\n#         self.val_loss = {'batch':[], 'epoch':[]}\n#         self.val_acc = {'batch':[], 'epoch':[]}\n\n#     def on_batch_end(self, batch, logs={}):\n#         self.losses['batch'].append(logs.get('loss'))\n#         self.accuracy['batch'].append(logs.get('acc'))\n#         self.val_loss['batch'].append(logs.get('val_loss'))\n#         self.val_acc['batch'].append(logs.get('val_acc'))\n\n#     def on_epoch_end(self, batch, logs={}):\n#         self.losses['epoch'].append(logs.get('loss'))\n#         self.accuracy['epoch'].append(logs.get('acc'))\n#         self.val_loss['epoch'].append(logs.get('val_loss'))\n#         self.val_acc['epoch'].append(logs.get('val_acc'))\n\n#     def loss_plot(self, loss_type):\n#         iters = range(len(self.losses[loss_type]))\n#         plt.figure()\n#         # acc\n#         plt.plot(iters, self.accuracy[loss_type], 'r', label='test acc')\n#         # loss\n#         plt.plot(iters, self.losses[loss_type], 'g', label='test loss')\n# #         if loss_type == 'epoch':\n# #             # val_acc\n# #             plt.plot(iters, self.val_acc[loss_type], 'b', label='test acc')\n# #             # val_loss\n# #             plt.plot(iters, self.val_loss[loss_type], 'k', label='test loss')\n#         plt.grid(True)\n#         plt.xlabel(loss_type)\n#         plt.ylabel('acc-loss')\n#         plt.legend(loc=\"upper right\")\n#         plt.show()\n        \n","metadata":{"execution":{"iopub.status.busy":"2022-06-01T07:15:38.117475Z","iopub.execute_input":"2022-06-01T07:15:38.117960Z","iopub.status.idle":"2022-06-01T07:15:38.125173Z","shell.execute_reply.started":"2022-06-01T07:15:38.117791Z","shell.execute_reply":"2022-06-01T07:15:38.124544Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# 创建一个实例history","metadata":{}},{"cell_type":"code","source":"# history = LossHistory()","metadata":{"execution":{"iopub.status.busy":"2022-06-01T07:15:38.126787Z","iopub.execute_input":"2022-06-01T07:15:38.127095Z","iopub.status.idle":"2022-06-01T07:15:38.136504Z","shell.execute_reply.started":"2022-06-01T07:15:38.127036Z","shell.execute_reply":"2022-06-01T07:15:38.135750Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# create callbacks list\n# 回调函数文档  https://keras.io/zh/callbacks/\nfrom keras.callbacks import (ModelCheckpoint, LearningRateScheduler,\n                             EarlyStopping, ReduceLROnPlateau,CSVLogger)\n\nepochs = 20; batch_size = 32\n\n# ModelCheckpoint(filepath, monitor='val_loss', verbose=0, save_best_only=False, \n#                 save_weights_only)\n# filepath：保存模型的路径      monitor: 被监测的数据    verbose=0:不显示信息  verbose=1:显示进度条\n#  save_best_only=True， 被监测数据的最佳模型就不会被覆盖    因为用的是val_loss,所以mode='min'\n# save_weights_only = True只有模型的权重被保存\ncheckpoint = ModelCheckpoint('../working/Resnet50.h5', monitor='val_loss', verbose=1, \n                             save_best_only=True, mode='min', save_weights_only = True)\nreduceLROnPlat = ReduceLROnPlateau(monitor='val_loss', factor=0.5, patience=4, \n                                   verbose=1, mode='min', epsilon=0.0001)\n# 所以训练12个epoch就自动停止了\nearly = EarlyStopping(monitor=\"val_loss\", \n                      mode=\"min\", \n                      patience=9)\n\ncsv_logger = CSVLogger(filename='../working/training_log.csv',\n                       separator=',',  #用“,”进行隔离.csv文件中的元素字符串\n                       append=True)  #不覆盖原有文件\n# callbacks_list = [checkpoint, csv_logger, reduceLROnPlat, early]\n\ntrain_generator = My_Generator(train_x, train_y, 128, is_train=True)\ntrain_mixup = My_Generator(train_x, train_y, batch_size, is_train=True, mix=False, augment=True)\nvalid_generator = My_Generator(valid_x, valid_y, batch_size, is_train=False)\n\nmodel = create_model(\n    input_shape=(SIZE,SIZE,3), \n    n_out=NUM_CLASSES)  #n_out是输出数量，设置为NUM_CLASSES,比较方便","metadata":{"execution":{"iopub.status.busy":"2022-06-01T07:15:38.138710Z","iopub.execute_input":"2022-06-01T07:15:38.138970Z","iopub.status.idle":"2022-06-01T07:15:50.861660Z","shell.execute_reply.started":"2022-06-01T07:15:38.138887Z","shell.execute_reply":"2022-06-01T07:15:50.860816Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# 有关QWK分数的计算\nfrom keras.callbacks import Callback\nclass QWKEvaluation(Callback):\n    def __init__(self, validation_data=(), batch_size=32, interval=1):\n        super(Callback, self).__init__()\n\n        self.interval = interval\n        self.batch_size = batch_size\n        self.valid_generator, self.y_val = validation_data\n        self.history = []\n\n    def on_epoch_end(self, epoch, logs={}):\n        if epoch % self.interval == 0:\n            y_pred = self.model.predict_generator(generator=self.valid_generator,\n                                                  steps=np.ceil(float(len(self.y_val)) / float(self.batch_size)),\n                                                  workers=1, use_multiprocessing=True,\n                                                  verbose=1)\n            def flatten(y):\n                return np.argmax(y, axis=1).reshape(-1)\n                # return np.sum(y.astype(int), axis=1) - 1\n            \n            score = cohen_kappa_score(flatten(self.y_val),\n                                      flatten(y_pred),\n                                      labels=[0,1,2,3,4],\n                                      weights='quadratic')\n#             print(flatten(self.y_val)[:5])\n#             print(flatten(y_pred)[:5])\n            print(\"\\n epoch: %d - QWK_score: %.6f \\n\" % (epoch+1, score))\n            self.history.append(score)\n            if score >= max(self.history):\n                print('save checkpoint: ', score)\n                self.model.save('../working/Resnet50_bestqwk.h5')\n\nqwk = QWKEvaluation(validation_data=(valid_generator, valid_y),\n                    batch_size=batch_size, interval=1)","metadata":{"execution":{"iopub.status.busy":"2022-06-01T07:15:50.863236Z","iopub.execute_input":"2022-06-01T07:15:50.863554Z","iopub.status.idle":"2022-06-01T07:15:50.883551Z","shell.execute_reply.started":"2022-06-01T07:15:50.863505Z","shell.execute_reply":"2022-06-01T07:15:50.881450Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# # warm up model\n# for layer in model.layers:\n#     layer.rainable = False      #这段代码的意思是将所有层冻结，权重不会更新，\n\n# for i in range(-5,0):\n#     model.layers[i].trainable = True     #这是让从倒数第5到倒数第1层解冻，可以更新权重\n\n# #     调用优化器：之前已经定义好了\n# model.compile(        \n#     loss='categorical_crossentropy',  #分类交叉熵，如【0，0，1】或【0.1, 0.2, 0.7】都是指的预测第三类\n#     # loss='binary_crossentropy',  二元交叉熵，用于二分类{0，1}\n#     optimizer=Adam(1e-3))\n\n# model.fit_generator(\n#     train_generator,\n#     steps_per_epoch=np.ceil(float(len(train_y)) / float(128)),\n#     epochs=3,\n#     workers=WORKERS, use_multiprocessing=True,\n#     verbose=1,\n#     callbacks=[qwk])","metadata":{"execution":{"iopub.status.busy":"2022-06-01T07:15:50.885027Z","iopub.execute_input":"2022-06-01T07:15:50.885536Z","iopub.status.idle":"2022-06-01T07:15:50.896765Z","shell.execute_reply.started":"2022-06-01T07:15:50.885482Z","shell.execute_reply":"2022-06-01T07:15:50.895763Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# 训练网络","metadata":{}},{"cell_type":"code","source":"# train all layers\nfor layer in model.layers:\n    layer.trainable = True\n\ncallbacks_list = [checkpoint, csv_logger, reduceLROnPlat, early, qwk]\n# model.compile()让模型解冻生效\nmodel.compile(loss='categorical_crossentropy',\n            # loss=kappa_loss,\n            # loss='binary_crossentropy',\n            # optimizer=Adam(lr=1e-4),\n            optimizer=AdamAccumulate(lr=1e-4, accum_iters=2),\n            metrics=['accuracy'])\n\n# model.fit_generator(\n#     train_mixup,\n#     steps_per_epoch=np.ceil(float(len(train_x)) / float(batch_size)),\n#     validation_data=valid_generator,\n#     validation_steps=np.ceil(float(len(valid_x)) / float(batch_size)),\n#     epochs=epochs,\n#     verbose=1,\n#     workers=1, use_multiprocessing=False,\n#     callbacks=[history])\nmodel.fit_generator(\n    train_mixup,\n    steps_per_epoch=np.ceil(float(len(train_x)) / float(batch_size)),\n    validation_data=valid_generator,\n    validation_steps=np.ceil(float(len(valid_x)) / float(batch_size)),\n    epochs=epochs,\n    verbose=1,\n    workers=1, use_multiprocessing=False,\n    callbacks=callbacks_list)","metadata":{"execution":{"iopub.status.busy":"2022-06-01T07:15:50.898390Z","iopub.execute_input":"2022-06-01T07:15:50.898877Z","iopub.status.idle":"2022-06-01T12:40:34.819393Z","shell.execute_reply.started":"2022-06-01T07:15:50.898654Z","shell.execute_reply":"2022-06-01T12:40:34.818348Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# 绘制acc-loss曲线","metadata":{}},{"cell_type":"code","source":"# history.loss_plot('epoch')","metadata":{"execution":{"iopub.status.busy":"2022-06-01T12:40:34.827767Z","iopub.execute_input":"2022-06-01T12:40:34.829958Z","iopub.status.idle":"2022-06-01T12:40:34.834897Z","shell.execute_reply.started":"2022-06-01T12:40:34.829896Z","shell.execute_reply":"2022-06-01T12:40:34.834177Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# 测试","metadata":{}},{"cell_type":"code","source":"submit = pd.read_csv('../input/aptos2019-blindness-detection/train.csv')\n# model.load_weights('../working/Resnet50.h5')\n# 从被保存的h5文件中,加载所有层的权重，其中h5文件里边有已经训练好的权重\nmodel.load_weights('../working/Resnet50_bestqwk.h5')\npredicted = []    #一个预测的空列表","metadata":{"execution":{"iopub.status.busy":"2022-06-01T12:40:34.839066Z","iopub.execute_input":"2022-06-01T12:40:34.841144Z","iopub.status.idle":"2022-06-01T12:40:37.757758Z","shell.execute_reply.started":"2022-06-01T12:40:34.841093Z","shell.execute_reply":"2022-06-01T12:40:37.756325Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# tqdm是Python进度条，可以在Python 长循环中添加一个进度提示信息\nfor i, name in tqdm(enumerate(submit['id_code'])):\n    path = os.path.join('../input/aptos2019-blindness-detection/train_images/', name+'.png')\n    image = cv2.imread(path)\n    image = cv2.resize(image, (SIZE, SIZE))\n    score_predict = model.predict((image[np.newaxis])/255)\n    \n    label_predict = np.argmax(score_predict)\n    # label_predict = score_predict.astype(int).sum() - 1\n#     在表格最后一栏添加“预测”\n    predicted.append(str(label_predict))","metadata":{"execution":{"iopub.status.busy":"2022-06-01T12:40:37.762509Z","iopub.execute_input":"2022-06-01T12:40:37.764639Z","iopub.status.idle":"2022-06-01T12:47:38.277493Z","shell.execute_reply.started":"2022-06-01T12:40:37.764588Z","shell.execute_reply":"2022-06-01T12:47:38.276580Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"submit['diagnosis'] = predicted\n# index=False：输出不显示索引\n# submit.to_csv('submission.csv', index=True)\nsubmit.to_csv('submission.csv', index=False)\nsubmit.head()","metadata":{"execution":{"iopub.status.busy":"2022-06-01T12:47:38.278746Z","iopub.execute_input":"2022-06-01T12:47:38.279038Z","iopub.status.idle":"2022-06-01T12:47:38.556030Z","shell.execute_reply.started":"2022-06-01T12:47:38.279001Z","shell.execute_reply":"2022-06-01T12:47:38.555410Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]}]}