猫狗图像识别

这里,我们介绍的是一个猫狗图像识别的一个任务。数据可以从kaggle网站上下载。其中包含了25000张毛和狗的图像(每个类别各12500张)。
在小样本中进行尝试

我们下面先尝试在一个小数据上进行训练,首先创建三个子集:每个类别各1000个样本的训练集、每个类别各500个样本的验证集和每个类别各500个样本的测试集。

import os, shutil

original_dataset_dir = ‘/media/erphm/DATA/kaggle猫狗识别/train‘    # 原始文解压目录
base_dir = ‘/media/erphm/DATA/kaggle猫狗识别/small_data‘     #保存较小数据集目录
os.mkdir(base_dir)  #创建新的文件夹

# 分别对应划分好的训练,验证和测试目录
train_dir = os.path.join(base_dir, ‘train‘)
os.mkdir(train_dir)
validation_dir = os.path.join(base_dir, ‘validation‘)
os.mkdir(validation_dir)
test_dir = os.path.join(base_dir, ‘test‘)
os.mkdir(test_dir)

#猫的训练目录
train_cats_dir = os.path.join(train_dir, ‘cats‘)
os.mkdir(train_cats_dir)

# 狗的训练目录
train_dogs_dir = os.path.join(train_dir, ‘dogs‘)
os.mkdir(train_dogs_dir)

# 猫的验证目录
validation_cats_dir = os.path.join(validation_dir, ‘cats‘)
os.mkdir(validation_cats_dir)

# 狗的验证目录
validation_dogs_dir = os.path.join(validation_dir, ‘dogs‘)
os.mkdir(validation_dogs_dir)

# 猫的测试目录
test_cats_dir = os.path.join(test_dir, ‘cats‘)
os.mkdir(test_cats_dir)

# 狗的测试目录
test_dogs_dir = os.path.join(test_dir, ‘dogs‘)
os.mkdir(test_dogs_dir)

# 将前1000张猫的图像复制到train_cats_dir
fnames = [‘cat.{}.jpg‘.format(i) for i in range(1000)]     #format函数通过{}来指点字符串处理的位置,储存为列表形式
for fname in fnames:
    src = os.path.join(original_dataset_dir, fname)
    dst = os.path.join(train_cats_dir, fname)
    shutil.copyfile(src, dst)    #copyfile实现将一个文件中的内容复制道另一个文件中去,src是来源文件;dst是目标文件

# 将剩下的500张图像复制到validation_cats_dir
fnames = [‘cat.{}.jpg‘.format(i) for i in range(1000, 1500)]
for fname in fnames:
    src = os.path.join(original_dataset_dir, fname)
    dst = os.path.join(validation_cats_dir, fname)
    shutil.copyfile(src, dst)
    
# 将接下来500张图片复制到test_cats_dir
fnames = [‘cat.{}.jpg‘.format(i) for i in range(1500, 2000)]
for fname in fnames:
    src = os.path.join(original_dataset_dir, fname)
    dst = os.path.join(test_cats_dir, fname)
    shutil.copyfile(src, dst)
    
# 将前1000张狗的图片复制到train_dogs_dir
fnames = [‘dog.{}.jpg‘.format(i) for i in range(1000)]
for fname in fnames:
    src = os.path.join(original_dataset_dir, fname)
    dst = os.path.join(train_dogs_dir, fname)
    shutil.copyfile(src, dst)
    
# 将接下来500张图像复制到validation_dogs_dir
fnames = [‘dog.{}.jpg‘.format(i) for i in range(1000, 1500)]
for fname in fnames:
    src = os.path.join(original_dataset_dir, fname)
    dst = os.path.join(validation_dogs_dir, fname)
    shutil.copyfile(src, dst)
    
# Copy next 500 dog images to test_dogs_dir
fnames = [‘dog.{}.jpg‘.format(i) for i in range(1500, 2000)]
for fname in fnames:
    src = os.path.join(original_dataset_dir, fname)
    dst = os.path.join(test_dogs_dir, fname)
    shutil.copyfile(src, dst)

1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81

1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81

通过代码的方式,把文件中的内容划分进入新的文件夹。下面,测试一下分组的实现情况。

print(‘total training cat images:‘, len(os.listdir(train_cats_dir)))    #os.listdir列举指定目录中的文件名
print(‘total training dog images:‘, len(os.listdir(train_dogs_dir)))
print(‘total validation cat images:‘, len(os.listdir(validation_cats_dir)))
print(‘total validation dog images:‘, len(os.listdir(validation_dogs_dir)))
print(‘total test cat images:‘, len(os.listdir(test_cats_dir)))
print(‘total test dog images:‘, len(os.listdir(test_dogs_dir)))

1
    2
    3
    4
    5
    6

1
    2
    3
    4
    5
    6

total training cat images: 1000
total training dog images: 1000
total validation cat images: 500
total validation dog images: 500
total test cat images: 500
total test dog images: 500
构建网络

from keras import layers
from keras import models

model = models.Sequential()
model.add(layers.Conv2D(32, (3, 3), activation=‘relu‘,
                        input_shape=(150, 150, 3)))
model.add(layers.MaxPooling2D((2, 2)))
model.add(layers.Conv2D(64, (3, 3), activation=‘relu‘))
model.add(layers.MaxPooling2D((2, 2)))
model.add(layers.Conv2D(128, (3, 3), activation=‘relu‘))
model.add(layers.MaxPooling2D((2, 2)))
model.add(layers.Conv2D(128, (3, 3), activation=‘relu‘))
model.add(layers.MaxPooling2D((2, 2)))
model.add(layers.Flatten())
model.add(layers.Dense(512, activation=‘relu‘))
model.add(layers.Dense(1, activation=‘sigmoid‘))

1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16

1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16

从代码中我们可以看到,我们采用的是conv2D和MaxPooling2D交替组合的形式。特征图的深度在逐渐增加(从32增加到128),而特征图的尺寸在逐渐减小(从150150减小到77),这几乎是所有卷积神经网络的模式。这样做的还有一个好处是最后使用Flatten的时候尺寸不会太大。

因为,猫狗识别任务是一个二分类任务,所以网络最后一层使用的是sigmoid激活的单一单元(大小为1的Dense层)。这个单元对某个类别的概率进行编码。

model.summary()

1

1

输出:

Layer (type)                 Output Shape              Param #   
=================================================================
conv2d_1 (Conv2D)            (None, 148, 148, 32)      896       
_________________________________________________________________
max_pooling2d_1 (MaxPooling2 (None, 74, 74, 32)        0         
_________________________________________________________________
conv2d_2 (Conv2D)            (None, 72, 72, 64)        18496     
_________________________________________________________________
max_pooling2d_2 (MaxPooling2 (None, 36, 36, 64)        0         
_________________________________________________________________
conv2d_3 (Conv2D)            (None, 34, 34, 128)       73856     
_________________________________________________________________
max_pooling2d_3 (MaxPooling2 (None, 17, 17, 128)       0         
_________________________________________________________________
conv2d_4 (Conv2D)            (None, 15, 15, 128)       147584    
_________________________________________________________________
max_pooling2d_4 (MaxPooling2 (None, 7, 7, 128)         0         
_________________________________________________________________
flatten_1 (Flatten)          (None, 6272)              0         
_________________________________________________________________
dense_1 (Dense)              (None, 512)               3211776   
_________________________________________________________________
dense_2 (Dense)              (None, 1)                 513       
=================================================================
Total params: 3,453,121
Trainable params: 3,453,121
Non-trainable params: 0

1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27

下面进行编译:

from keras import optimizers

model.compile(loss=‘binary_crossentropy‘,
              optimizer=optimizers.RMSprop(lr=1e-4),
              metrics=[‘acc‘])

1
    2
    3
    4
    5

1
    2
    3
    4
    5

数据处理

图片不能直接放入神经网络中进行学习,学习之前应该把数据格式化为经过预处理的浮点数张量。我们接下来数据处理大致分为4个方向:
(1) 读取图像文件
(2) 将JPEG文件解码为RGB像素网格
(3) 将这些像素网格转换为浮点数张量
(4) 将像素值(0-255范围内)缩放到[0,1]区间(神经网络喜欢处理较小的数据输入值)

Keras中的keras.preprocessing.image有包含ImageDataGenerator类,可以快速创建Python生成器,能够将硬盘上的图像文件自动转换为预处理好的张量批量,下面看一下代码:

from keras.preprocessing.image import ImageDataGenerator

train_datagen = ImageDataGenerator(rescale=1./255)   
test_datagen = ImageDataGenerator(rescale=1./255)

# 使用flow_from_directory()方法可以实例化一个针对图像batch的生成器
train_generator = train_datagen.flow_from_directory(
        
        train_dir,     # 目标目录
        
        target_size=(150, 150),      # 将所有图像大小调整为150*150
        batch_size=20,
        
        class_mode=‘binary‘)      # 因为使用了binary_crossentropy损失,所以需要使用二进制标签

validation_generator = test_datagen.flow_from_directory(
        validation_dir,
        target_size=(150, 150),
        batch_size=20,
        class_mode=‘binary‘)

1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20

1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20

来看一下其中一个生成器的输出:它生成150*150的RGB图像[形状为(20, 150, 150, 3)]与二进制标签[形状为(20,)]组成的批量。每个批量包含20个样本。注意,生成器会不停的生成这种批量,它会不断的循环目标文件夹中的图像,因此,你需要在某个时刻(break)迭代循环。

for data_batch, labels_batch in train_generator:
    print(‘data batch shape:‘, data_batch.shape)
    print(‘labels batch shape:‘, labels_batch.shape)
    break

1
    2
    3
    4

1
    2
    3
    4

输出为:
data batch shape: (20, 150, 150, 3)
labels batch shape: (20,)

利用生成器,我们可以让模型对数据进行拟合。我们使用fit_generator方法来拟合数据(在生成器上的效果和fit一样),他的第一个参数应该是Python生成器。因为数据是不断生成的,所以Keras模型需要知道每一论需要从生成器中抽取多少个样本,这就用到了step_per_epoch参数。
我们还可以向里面传入一个validataion_data参数,这个参数也可以是一个生成器,也可以是Numpy数组组成的元组,如果是生成器的话,还需要指定validation_steps参数,用来说明需要从验证的生成器中抽取多少个批次用于评估。

history = model.fit_generator(
      train_generator,
      steps_per_epoch=100,
      epochs=30,
      validation_data=validation_generator,
      validation_steps=50)

1
    2
    3
    4
    5
    6

1
    2
    3
    4
    5
    6

之后,我们可以把我们的模型保存一下

model.save(‘cats_and_dogs_small_1.h5‘)

1

下面分别绘制训练过程中模型在训练数据和验证数据长的损失函数和精度

import matplotlib.pyplot as plt

acc = history.history[‘acc‘]
val_acc = history.history[‘val_acc‘]
loss = history.history[‘loss‘]
val_loss = history.history[‘val_loss‘]

epochs = range(len(acc))

plt.plot(epochs, acc, ‘bo‘, label=‘Training acc‘)
plt.plot(epochs, val_acc, ‘b‘, label=‘Validation acc‘)
plt.title(‘Training and validation accuracy‘)
plt.legend()

plt.figure()

plt.plot(epochs, loss, ‘bo‘, label=‘Training loss‘)
plt.plot(epochs, val_loss, ‘b‘, label=‘Validation loss‘)
plt.title(‘Training and validation loss‘)
plt.legend()

plt.show()

1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22

1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22

在这里插入图片描述

在这里插入图片描述

从图中我们可以看到过拟合的现象,训练精度随着时间线性增加,直到接近100%,而验证精度则基本停留在70-72%,验证损失在5轮后就达到最小值,然后保持不变,而训练损失则一直线性下降,直到接近于0.
因为训练样本相对来说比较少(2000个),所以过拟合是一个十分重要的问题,下面介绍一个针对计算机视觉处理过拟合的新方法,在深度学习处理图像时几乎都会用到的方法,数据增强
使用数据增强

在Keras中,我们可以通过对ImageDataGenerator实例读取的图像执行多次随机变换来实现。

datagen = ImageDataGenerator(
      rotation_range=40,
      width_shift_range=0.2,
      height_shift_range=0.2,
      shear_range=0.2,
      zoom_range=0.2,
      horizontal_flip=True,
      fill_mode=‘nearest‘)

1
    2
    3
    4
    5
    6
    7
    8

1
    2
    3
    4
    5
    6
    7
    8

显示几个随机增强之后的训练图像

from keras.preprocessing import image # 图像预处理的工具模块

fnames = [os.path.join(train_cats_dir, fname) for fname in os.listdir(train_cats_dir)]

img_path = fnames[3] # 选择一个图像进行数据增强

img = image.load_img(img_path, target_size=(150, 150)) # 读取图像并调整大小

x = image.img_to_array(img)   # 将其转换为形状(150, 150, 3)的Numpy数组

x = x.reshape((1,) + x.shape)  # 将其形状改为(1, 150, 150, 3)

# 生成随机变换后的图像批量
# 循环是无限的,因此你需要在某个时刻终止循环
i = 0
for batch in datagen.flow(x, batch_size=1):
    plt.figure(i)
    imgplot = plt.imshow(image.array_to_img(batch[0]))
    i += 1
    if i % 4 == 0:
        break

plt.show()

1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23

1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

使用这种数据增强的办法来训练新的网络,那么网络将不会两次看到一样的输入。但网络看到的输入仍然是高度相关的,因为这些输入都来自于少量的原始图像,无法生成新的信息,只能混合现有的信息。因此,这种方法不能完全消除过拟合。

下面向模型中添加Dropou层,进一步降低过拟合。

model = models.Sequential()
model.add(layers.Conv2D(32, (3, 3), activation=‘relu‘,
                        input_shape=(150, 150, 3)))
model.add(layers.MaxPooling2D((2, 2)))
model.add(layers.Conv2D(64, (3, 3), activation=‘relu‘))
model.add(layers.MaxPooling2D((2, 2)))
model.add(layers.Conv2D(128, (3, 3), activation=‘relu‘))
model.add(layers.MaxPooling2D((2, 2)))
model.add(layers.Conv2D(128, (3, 3), activation=‘relu‘))
model.add(layers.MaxPooling2D((2, 2)))
model.add(layers.Flatten())
model.add(layers.Dropout(0.5))
model.add(layers.Dense(512, activation=‘relu‘))
model.add(layers.Dense(1, activation=‘sigmoid‘))

model.compile(loss=‘binary_crossentropy‘,
              optimizer=optimizers.RMSprop(lr=1e-4),
              metrics=[‘acc‘])

1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18

1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18

下面使用数据增强训练带有Dropout的网络:

train_datagen = ImageDataGenerator(
    rescale=1./255,
    rotation_range=40,
    width_shift_range=0.2,
    height_shift_range=0.2,
    shear_range=0.2,
    zoom_range=0.2,
    horizontal_flip=True,)

test_datagen = ImageDataGenerator(rescale=1./255)

train_generator = train_datagen.flow_from_directory(
        # This is the target directory
        train_dir,
        target_size=(150, 150),  # 将所有图像大小调整为(150, 150)
        batch_size=32,
        class_mode=‘binary‘)  # 因为使用binary_crossentropy损失,所以需要使用二进制标签

validation_generator = test_datagen.flow_from_directory(
        validation_dir,
        target_size=(150, 150),
        batch_size=32,
        class_mode=‘binary‘)

history = model.fit_generator(
      train_generator,
      steps_per_epoch=100,
      epochs=100,
      validation_data=validation_generator,
      validation_steps=50)

1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30

1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30

之后我们保存一下训练好的模型:

model.save(‘cats_and_dogs_small_2.h5‘)

1

1

我们重新绘制一下图像:

acc = history.history[‘acc‘]
val_acc = history.history[‘val_acc‘]
loss = history.history[‘loss‘]
val_loss = history.history[‘val_loss‘]

epochs = range(len(acc))

plt.plot(epochs, acc, ‘bo‘, label=‘Training acc‘)
plt.plot(epochs, val_acc, ‘b‘, label=‘Validation acc‘)
plt.title(‘Training and validation accuracy‘)
plt.legend()

plt.figure()

plt.plot(epochs, loss, ‘bo‘, label=‘Training loss‘)
plt.plot(epochs, val_loss, ‘b‘, label=‘Validation loss‘)
plt.title(‘Training and validation loss‘)
plt.legend()

plt.show()

1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20

1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20

在这里插入图片描述
在这里插入图片描述

通过观察这个绘图结果,可以发现使用数据增强和Dropout之后,模型过拟合情况大大减少,训练曲线紧跟着预测曲线。比之前没有使用正则化和数据增强的办法,效果要好很多。

更多精彩内容,欢迎关注我的微信公众号:数据瞎分析

在这里插入图片描述
---------------------  
作者:Einstellung  
来源:CSDN  
原文:https://blog.csdn.net/Einstellung/article/details/82773170  
版权声明:本文为博主原创文章,转载请附上博文链接!

原文地址:https://www.cnblogs.com/shuimuqingyang/p/10413182.html

时间: 2024-11-12 14:52:22

猫狗图像识别的相关文章

pytorch实现kaggle猫狗识别

参考:https://blog.csdn.net/weixin_37813036/article/details/90718310 kaggle是一个为开发商和数据科学家提供举办机器学习竞赛.托管数据库.编写和分享代码的平台,在这上面有非常多的好项目.好资源可供机器学习.深度学习爱好者学习之用.碰巧最近入门了一门非常的深度学习框架:pytorch(如果你对pytorch不甚了解,请点击这里),所以今天我和大家一起用pytorch实现一个图像识别领域的入门项目:猫狗图像识别.深度学习的基础就是数据

卷积神经网络入门(1) 识别猫狗

一下来自知乎 按照我的理解,CNN的核心其实就是卷积核的作用,只要明白了这个问题,其余的就都是数学坑了(当然,相比较而言之后的数学坑更难). 如果学过数字图像处理,对于卷积核的作用应该不陌生,比如你做一个最简单的方向滤波器,那就是一个二维卷积核,这个核其实就是一个模板,利用这个模板再通过卷积计算的定义就可以计算出一幅新的图像,新的图像会把这个卷积核所体现的特征突出显示出来.比如这个卷积核可以侦测水平纹理,那卷积出来的图就是原图水平纹理的图像. 现在假设要做一个图像的分类问题,比如辨别一个图像里是

当当变“猴子”后能敌“猫狗”?

后知后觉虽然能看清竞争形势,可以谋而后动,弥补自身不足,增强自身竞争力,不过这也意味着将失去先机,与那些先行者的差距越来越大.即使通过让自己处于一个完美态势,却难敌用户"先入为主"的观念.已经成立15年的当当就面对着这样一个难题,在阿里.京东接连发力,独霸网购市场前两名的态势下,当当却还在和苏宁易购.1号店.亚马逊等在第二阵营中惨烈搏杀. 即使当下通过各种手段提升自己的"逼格",但终究慢了对手好几步,想让自己处于第一集团军中,再多的挣扎似乎也是徒然.而在转型的路上,

猫狗队列

1,add方法将Dog实例或Cat实例加入猫狗队列 2,pollAll将猫狗队列中的实例按插入顺序弹出 3,pollDog将猫狗队列中的Dog实例按插入顺序弹出 4,pollCat将猫狗队列中的Cat实例按插入顺序弹出 5,isEmpty判断猫狗队列中是否还有实例 6,isDogEmpty判断猫狗队列中是否还有Dog实例 7,isCatEmpty判断猫狗队列中是否还有Cat实例 #!/usr/bin/env python3 # --*-- coding: utf-8 --*-- from col

自己训练卷积模型实现猫狗

原数据集:包含 25000张猫狗图像,两个类别各有12500 新数据集:猫.狗 (照片大小不一样) 训练集:各1000个样本 验证集:各500个样本 测试集:各500个样本 # 将图像复制到训练.验证和测试的目录 import os,shutil orginal_dataset_dir = 'kaggle_original_data/train' base_dir = 'cats_and_dogs_small' os.mkdir(base_dir)#保存新数据集的目录 train_dir = o

6.猫狗队列问题

问题:宠物.狗.猫的类如下 class Pet { private String type; public Pet(String type) { this.type = type; } public String getType() { return this.type; } public String toString(){ return this.getType(); } } class Dog extends Pet { public Dog(String type) { super(ty

类入门例-猫狗互咬

以下例子演示动物类的写法,其中用到了构造函数. 在Main函数中,使用动物类创造出一只猫,一条狗. 猫狗互相攻击,直到一方死去. 代码如下: class Animal { public string name;//动物名称 public int hp, attack, speed;//动物血量.攻击力.攻击速度 public Animal(string n,int life,int force,int s) { name = n; hp = life; attack = force; speed

人们捐款保护猫狗是为了自己

无利不起早 做一件事情绝对有一个利益 人们捐款去救助猫狗,只是为了自己.感到满足. 去除掉好坏,善恶的定义,只从行为上来看,在没有善恶定义之前,也会做这些事情,利益无论是为了自己还是为了更大的群体.只是被动的被环境选择来做这些事情 如果生命是环境选择出来的,环境是否也会被生命选择. 其实没有所谓选择不选择,天地不仁. 原文地址:https://www.cnblogs.com/yangzihong/p/11562210.html

python练习:寒冰猴子狐狸,猫狗咬架

python练习:寒冰猴子狐狸,猫狗咬架 一,寒冰猴子狐狸 class Person: def __init__(self, na, gen, age, fig): self.name = na self.gender = gen self.age = age self.fight =fig def grassland(self): """注释:草丛战斗,消耗200战斗力""" self.fight = self.fight - 200 def