UNet详解(附图文和代码实现)

人工智能91

卷积神经网络被大规模的应用在分类任务中,输出的结果是整个图像的类标签。但是UNet是像素级分类,输出的则是每个像素点的类别,且不同类别的像素会显示不同颜色,UNet常常用在生物医学图像上,而该任务中图片数据往往较少。所以,Ciresan等人训练了一个卷积神经网络,用滑动窗口提供像素的周围区域(patch)作为输入来预测每个像素的类标签。这个网络有两个优点:(1)输出结果可以定位出目标类别的位置;(2)由于输入的训练数据是patches,这样就相当于进行了数据增强,从而解决了生物医学图像数量少的问题。

但是,采用该方法的神经网络也有两个很明显的缺点:(1)它很慢,因为这个网络必须训练每个patch,并且因为patch之间的重叠有很多冗余,这样会导致同样特征被多次训练,造成资源的浪费,导致训练时间的加长且效率也会有所降低,也有人会问神经网络经过多次训练这个特征后,会对这个特征的印象加深,从而准确率也会上升,但是举个例子一个图片复制50张,用这50张图片去训练网络,虽说数据集增大了,可是导致的后果是神经网络会出现过拟合,也就是说神经网络对训练图片很熟悉,可是换了一张图片,神经网络就有可能分辨不出来了。(2)定位准确性和获取上下文信息不可兼得,大的patches需要更多的max-pooling,这样会减少定位准确性,因为最大池化会丢失目标像素和周围像素之间的空间关系,而小patches只能看到很小的局部信息,包含的背景信息不够。

UNet主要贡献是在U型结构上,该结构可以使它使用更少的训练图片的同时,且分割的准确度也不会差,UNet的网络结构如下图:

UNet详解(附图文和代码实现)
(1)UNet采用全卷积神经网络。
(2)左边网络为特征提取网络:使用conv和pooling
(3)右边网络为特征融合网络:使用上采样产生的特征图与左侧特征图进行concatenate操作。(pooling层会丢失图像信息和降低图像分辨率且是永久性的,对于图像分割任务有一些影响,对图像分类任务的影响不大,为什么要做上采样呢?上采样可以让包含高级抽象特征低分辨率图片在保留高级抽象特征的同时变为高分辨率,然后再与左边低级表层特征高分辨率图片进行concatenate操作)
(4)最后再经过两次卷积操作,生成特征图,再用两个卷积核大小为1*1的卷积做分类得到最后的两张heatmap,例如第一张表示第一类的得分,第二张表示第二类的得分heatmap,然后作为softmax函数的输入,算出概率比较大的softmax,然后再进行loss,反向传播计算。

; Unet模型的代码实现(基于keras):

def get_unet():
    inputs = Input((img_rows, img_cols, 1))
    conv1 = Conv2D(32, (3, 3), activation='relu', padding='same')(inputs)
    conv1 = Conv2D(32, (3, 3), activation='relu', padding='same')(conv1)
    pool1 = MaxPooling2D(pool_size=(2, 2))(conv1)

    conv2 = Conv2D(64, (3, 3), activation='relu', padding='same')(pool1)
    conv2 = Conv2D(64, (3, 3), activation='relu', padding='same')(conv2)
    pool2 = MaxPooling2D(pool_size=(2, 2))(conv2)

    conv3 = Conv2D(128, (3, 3), activation='relu', padding='same')(pool2)
    conv3 = Conv2D(128, (3, 3), activation='relu', padding='same')(conv3)
    pool3 = MaxPooling2D(pool_size=(2, 2))(conv3)

    conv4 = Conv2D(256, (3, 3), activation='relu', padding='same')(pool3)
    conv4 = Conv2D(256, (3, 3), activation='relu', padding='same')(conv4)
    pool4 = MaxPooling2D(pool_size=(2, 2))(conv4)

    conv5 = Conv2D(512, (3, 3), activation='relu', padding='same')(pool4)
    conv5 = Conv2D(512, (3, 3), activation='relu', padding='same')(conv5)

    up6 = concatenate([Conv2DTranspose(256, (2, 2), strides=(
        2, 2), padding='same')(conv5), conv4], axis=3)

    conv6 = Conv2D(256, (3, 3), activation='relu', padding='same')(up6)
    conv6 = Conv2D(256, (3, 3), activation='relu', padding='same')(conv6)

    up7 = concatenate([Conv2DTranspose(128, (2, 2), strides=(
        2, 2), padding='same')(conv6), conv3], axis=3)

    conv7 = Conv2D(128, (3, 3), activation='relu', padding='same')(up7)
    conv7 = Conv2D(128, (3, 3), activation='relu', padding='same')(conv7)

    up8 = concatenate([Conv2DTranspose(64, (2, 2), strides=(
        2, 2), padding='same')(conv7), conv2], axis=3)

    conv8 = Conv2D(64, (3, 3), activation='relu', padding='same')(up8)
    conv8 = Conv2D(64, (3, 3), activation='relu', padding='same')(conv8)

    up9 = concatenate([Conv2DTranspose(32, (2, 2), strides=(
        2, 2), padding='same')(conv8), conv1], axis=3)

    conv9 = Conv2D(32, (3, 3), activation='relu', padding='same')(up9)
    conv9 = Conv2D(32, (3, 3), activation='relu', padding='same')(conv9)

    conv10 = Conv2D(1, (1, 1), activation='sigmoid')(conv9)

    model = Model(inputs=[inputs], outputs=[conv10])

    model.compile(optimizer=Adam(lr=1e-5),
                  loss=dice_coef_loss, metrics=[dice_coef])

    return model

Unet的代码实现(pytorch版)

"""
这是根据UNet模型搭建出的一个基本网络结构
输入和输出大小是一样的,可以根据需求进行修改
"""
import torch
import torch.nn as nn
from torch.nn import functional as F

class Conv(nn.Module):
    def __init__(self, C_in, C_out):
        super(Conv, self).__init__()
        self.layer = nn.Sequential(

            nn.Conv2d(C_in, C_out, 3, 1, 1),
            nn.BatchNorm2d(C_out),

            nn.Dropout(0.3),
            nn.LeakyReLU(),

            nn.Conv2d(C_out, C_out, 3, 1, 1),
            nn.BatchNorm2d(C_out),

            nn.Dropout(0.4),
            nn.LeakyReLU(),
        )

    def forward(self, x):
        return self.layer(x)

class DownSampling(nn.Module):
    def __init__(self, C):
        super(DownSampling, self).__init__()
        self.Down = nn.Sequential(

            nn.Conv2d(C, C, 3, 2, 1),
            nn.LeakyReLU()
        )

    def forward(self, x):
        return self.Down(x)

class UpSampling(nn.Module):

    def __init__(self, C):
        super(UpSampling, self).__init__()

        self.Up = nn.Conv2d(C, C // 2, 1, 1)

    def forward(self, x, r):

        up = F.interpolate(x, scale_factor=2, mode="nearest")
        x = self.Up(up)

        return torch.cat((x, r), 1)

class UNet(nn.Module):

    def __init__(self):
        super(UNet, self).__init__()

        self.C1 = Conv(3, 64)
        self.D1 = DownSampling(64)
        self.C2 = Conv(64, 128)
        self.D2 = DownSampling(128)
        self.C3 = Conv(128, 256)
        self.D3 = DownSampling(256)
        self.C4 = Conv(256, 512)
        self.D4 = DownSampling(512)
        self.C5 = Conv(512, 1024)

        self.U1 = UpSampling(1024)
        self.C6 = Conv(1024, 512)
        self.U2 = UpSampling(512)
        self.C7 = Conv(512, 256)
        self.U3 = UpSampling(256)
        self.C8 = Conv(256, 128)
        self.U4 = UpSampling(128)
        self.C9 = Conv(128, 64)

        self.Th = torch.nn.Sigmoid()
        self.pred = torch.nn.Conv2d(64, 3, 3, 1, 1)

    def forward(self, x):

        R1 = self.C1(x)
        R2 = self.C2(self.D1(R1))
        R3 = self.C3(self.D2(R2))
        R4 = self.C4(self.D3(R3))
        Y1 = self.C5(self.D4(R4))

        O1 = self.C6(self.U1(Y1, R4))
        O2 = self.C7(self.U2(O1, R3))
        O3 = self.C8(self.U3(O2, R2))
        O4 = self.C9(self.U4(O3, R1))

        return self.Th(self.pred(O4))

if __name__ == '__main__':
    a = torch.randn(2, 3, 256, 256)
    net = UNet()
    print(net(a).shape)

Unet的代码实现(TensorFlow版)


import tensorflow as tf
import tensorflow.contrib.slim as slim

def lrelu(x):
    return tf.maximum(x * 0.2, x)

activation_fn=lrelu

def UNet(inputs, reg):
    conv1 = slim.conv2d(inputs, 32, [3, 3], rate=1, activation_fn=activation_fn, scope='g_conv1_1', weights_regularizer=reg)
    conv1 = slim.conv2d(conv1, 32, [3, 3], rate=1, activation_fn=activation_fn, scope='g_conv1_2',weights_regularizer=reg)
    pool1 = slim.max_pool2d(conv1, [2, 2], padding='SAME')

    conv2 = slim.conv2d(pool1, 64, [3, 3], rate=1, activation_fn=activation_fn, scope='g_conv2_1',weights_regularizer=reg)
    conv2 = slim.conv2d(conv2, 64, [3, 3], rate=1, activation_fn=activation_fn, scope='g_conv2_2',weights_regularizer=reg)
    pool2 = slim.max_pool2d(conv2, [2, 2], padding='SAME')

    conv3 = slim.conv2d(pool2, 128, [3, 3], rate=1, activation_fn=activation_fn, scope='g_conv3_1',weights_regularizer=reg)
    conv3 = slim.conv2d(conv3, 128, [3, 3], rate=1, activation_fn=activation_fn, scope='g_conv3_2',weights_regularizer=reg)
    pool3 = slim.max_pool2d(conv3, [2, 2], padding='SAME')

    conv4 = slim.conv2d(pool3, 256, [3, 3], rate=1, activation_fn=activation_fn, scope='g_conv4_1',weights_regularizer=reg)
    conv4 = slim.conv2d(conv4, 256, [3, 3], rate=1, activation_fn=activation_fn, scope='g_conv4_2',weights_regularizer=reg)
    pool4 = slim.max_pool2d(conv4, [2, 2], padding='SAME')

    conv5 = slim.conv2d(pool4, 512, [3, 3], rate=1, activation_fn=activation_fn, scope='g_conv5_1',weights_regularizer=reg)
    conv5 = slim.conv2d(conv5, 512, [3, 3], rate=1, activation_fn=activation_fn, scope='g_conv5_2',weights_regularizer=reg)

    up6 = upsample_and_concat(conv5, conv4, 256, 512)
    conv6 = slim.conv2d(up6, 256, [3, 3], rate=1, activation_fn=activation_fn, scope='g_conv6_1',weights_regularizer=reg)
    conv6 = slim.conv2d(conv6, 256, [3, 3], rate=1, activation_fn=activation_fn, scope='g_conv6_2',weights_regularizer=reg)

    up7 = upsample_and_concat(conv6, conv3, 128, 256)
    conv7 = slim.conv2d(up7, 128, [3, 3], rate=1, activation_fn=activation_fn, scope='g_conv7_1',weights_regularizer=reg)
    conv7 = slim.conv2d(conv7, 128, [3, 3], rate=1, activation_fn=activation_fn, scope='g_conv7_2',weights_regularizer=reg)

    up8 = upsample_and_concat(conv7, conv2, 64, 128)
    conv8 = slim.conv2d(up8, 64, [3, 3], rate=1, activation_fn=activation_fn, scope='g_conv8_1',weights_regularizer=reg)
    conv8 = slim.conv2d(conv8, 64, [3, 3], rate=1, activation_fn=activation_fn, scope='g_conv8_2',weights_regularizer=reg)

    up9 = upsample_and_concat(conv8, conv1, 32, 64)
    conv9 = slim.conv2d(up9, 32, [3, 3], rate=1, activation_fn=activation_fn, scope='g_conv9_1', weights_regularizer=reg)
    conv9 = slim.conv2d(conv9, 32, [3, 3], rate=1, activation_fn=activation_fn, scope='g_conv9_2',weights_regularizer=reg)
    print("conv9.shape:{}".format(conv9.get_shape()))

    type='UNet_1X'
    with tf.variable_scope(name_or_scope="output"):
        if type=='UNet_3X':
            conv10 = slim.conv2d(conv9, 27, [1, 1], rate=1, activation_fn=None, scope='g_conv10',weights_regularizer=reg)
            out = tf.depth_to_space(conv10, 3)
        if type=='UNet_1X':
            out = slim.conv2d(conv9, 6, [1, 1], rate=1, activation_fn=None, scope='g_conv10',weights_regularizer=reg)
    return out

def upsample_and_concat(x1, x2, output_channels, in_channels):
    pool_size = 2
    deconv_filter = tf.Variable(tf.truncated_normal([pool_size, pool_size, output_channels, in_channels], stddev=0.02))
    deconv = tf.nn.conv2d_transpose(x1, deconv_filter, tf.shape(x2), strides=[1, pool_size, pool_size, 1])

    deconv_output = tf.concat([deconv, x2], 3)
    deconv_output.set_shape([None, None, None, output_channels * 2])
    return deconv_output

if __name__=="__main__":
    weight_decay=0.001
    reg = slim.l2_regularizer(scale=weight_decay)
    inputs = tf.ones(shape=[4, 256, 256, 3])
    out=UNet(inputs,reg)
    print("net1.shape:{}".format(inputs.get_shape()))
    print("out.shape:{}".format(out.get_shape()))
    with tf.Session() as sess:
        sess.run(tf.global_variables_initializer())

Original: https://blog.csdn.net/weixin_45074568/article/details/114901600
Author: liiiiiiiiiiiiike
Title: UNet详解(附图文和代码实现)



相关阅读

Title: 车辆运动控制(2)车辆横摆动力学建模

车辆运动控制(2)车辆横摆动力学建模

1. 简介

车辆整车动力学模型一般包括用于分析:

  • 车辆 平顺性质量-弹簧-阻尼模型
  • 车辆 操纵稳定性 的 *车辆-轮胎模型

两者研究的侧重点不同
平顺性分析的重点是车辆的悬架特性
而车辆操纵稳定性分析的重点是车辆纵向及侧向动力学特性

主要研究目标是使 车辆快速而稳定地跟踪期望路径,属于车辆操纵稳定性问题
因此对于悬架特性不做深人探究

2. 假设条件

这里所建立的动力学模型主要是作为模型预测控制器中的预测模型使用
需要在较为准确地描述车辆动力学过程的基础上尽可能进行简化,以减少控制算法的计算量

因此在进行车辆动力学建模时,进行以下理想化的假设:

  1. 假设无人驾驶车辆在平坦路面上行驶,忽略车辆垂向运动
  2. 悬架系统及车辆是刚性的,忽略悬架运动及其对耦合关系的影响
  3. 只考虑 纯侧偏 轮胎特性,忽略轮胎力的纵横向耦合关系
  4. 单轨模型 来描述车辆运动,不考虑载荷的左右转移
  5. 假设车辆行驶速度变化缓慢,忽略前后轴的载荷转移
  6. 忽略纵向和横向空气动力学

3. 单轨模型

基于上述假设可以得到单轨模型,利用其来分析车辆横摆动力学模型,如图:
UNet详解(附图文和代码实现)

对车辆进行受力分析,根据其受力平衡和力矩平衡可以得到:

  • 在x x x轴方向上:
    m ( v ˙ x − v y φ ˙ ) = F x f cos ⁡ ( δ f ) − F y f sin ⁡ ( δ f ) + F x r − F d i s s p m(\dot{v}x-v_y\dot{\varphi})=F{xf}\cos(\delta_f)-F_{yf}\sin(\delta_f)+F_{xr}-F_{dissp}m (v ˙x ​−v y ​φ˙​)=F x f ​cos (δf ​)−F y f ​sin (δf ​)+F x r ​−F d i s s p ​
  • 在y y y轴方向上:
    m ( v ˙ y + v x φ ˙ ) = F x f sin ⁡ ( δ f ) + F y f cos ⁡ ( δ f ) + F y r m(\dot{v}y+v_x\dot{\varphi})=F{xf}\sin(\delta_f)+F_{yf}\cos(\delta_f)+F_{yr}m (v ˙y ​+v x ​φ˙​)=F x f ​sin (δf ​)+F y f ​cos (δf ​)+F y r ​
  • 绕z z z轴方向上:
    I z φ ¨ = l f [ F x f sin ⁡ ( δ f ) + F y f cos ⁡ ( δ f ) ] − l r F y r I_z\ddot{\varphi}=l_f[F_{xf}\sin(\delta_f)+F_{yf}\cos(\delta_f)]-l_rF_{yr}I z ​φ¨​=l f ​[F x f ​sin (δf ​)+F y f ​cos (δf ​)]−l r ​F y r ​

符号含义符号含义符号含义
x x x

轴沿车辆纵轴
y y y

轴与车辆纵轴方向垂直
z z z

轴满足右手法则,垂直于
x o y xoy x o y

且向上
m m m

车辆质量
v x v_x v x ​

车体坐标系下质心的纵向速度
v y v_y v y ​

车体坐标系下质心的侧向速度
φ ˙ \dot{\varphi}φ˙​

车辆的横摆角的变化量
F x f , F x r F_{xf},F_{xr}F x f ​,F x r ​

车辆前、后轴上轮胎纵向力的合力
F y f , F y r F_{yf},F_{yr}F y f ​,F y r ​

车辆前、后轴上轮胎侧向力的合力
δ f \delta_f δf ​

前轮偏角
F d i s s p F_{dissp}F d i s s p ​

车辆在纵向上受到阻力的合力
I z I_z I z ​

车辆绕
z z z

轴的转动惯量
α \alpha α

轮胎侧偏角
l f l_f l f ​

质心到前轴的距离
l r l_r l r ​

质心到后轴的距离

若忽略前轮驱动力F x f F_{xf}F x f ​对车辆横摆运动的影响,即 F x f sin ⁡ ( δ f ) ≈ 0 F_{xf}\sin(\delta_f)≈0 F x f ​sin (δf ​)≈0,上三式可写为:
{ v ˙ x = v y φ ˙ + F x m v ˙ y = − v x φ ˙ + 1 m [ F y f cos ⁡ ( δ f ) + F y r ] φ ¨ = 1 I z ( l f F y f cos ⁡ ( δ f ) − l r F y r ) (14) \begin{cases} \dot{v}x=v_y\dot{\varphi} +\frac{F_x}{m}\\ \dot{v}_y=-v_x\dot{\varphi} +\frac{1}{m}[F{yf}\cos(\delta_f)+F_{yr}]\\ \ddot{\varphi}=\frac{1}{I_z}(l_fF_{yf}\cos(\delta_f)-l_rF_{yr}) \end{cases} \tag{14}⎩⎪⎪⎪⎪⎪⎪⎨⎪⎪⎪⎪⎪⎪⎧​v ˙x ​=v y ​φ˙​+m F x ​​v ˙y ​=−v x ​φ˙​+m 1 ​[F y f ​cos (δf ​)+F y r ​]φ¨​=I z ​1 ​(l f ​F y f ​cos (δf ​)−l r ​F y r ​)​(1 4 )

其中 F x = F x f cos ⁡ ( δ f ) − F y f sin ⁡ ( δ f ) + F x r − F d i s s p F_x=F_{xf}\cos(\delta_f)-F_{yf}\sin(\delta_f)+F_{xr}-F_{dissp}F x ​=F x f ​cos (δf ​)−F y f ​sin (δf ​)+F x r ​−F d i s s p ​,为轮胎受到的横、纵向力在车体坐标系x x x轴上的合力
当以前轮驱动的车辆作为研究目标时,认为后轮驱动力 F x r = 0 F_{xr}=0 F x r ​=0
轮胎受到车体坐标系x x x轴上的合力
F x = F x f cos ⁡ ( δ f ) − F y f sin ⁡ ( δ f ) − F d i s s p (15) F_x=F_{xf}\cos(\delta_f)-F_{yf}\sin(\delta_f)-F_{dissp}\tag{15}F x ​=F x f ​cos (δf ​)−F y f ​sin (δf ​)−F d i s s p ​(1 5 )

当纵向速度 v x v_x v x ​恒定,即 v ˙ x ≈ 0 \dot{v}x≈0 v ˙x ​≈0,则根据公式14
得到2自由度的车辆横摆动力学微分方程:
{ v ˙ y = − v x φ ˙ + 1 m [ F y f cos ⁡ ( δ f ) + F y r ] φ ¨ = 1 I z ( l f F y f cos ⁡ ( δ f ) − l r F y r ) (16) \begin{cases} \dot{v}_y=-v_x\dot{\varphi} +\frac{1}{m}[F
{yf}\cos(\delta_f)+F_{yr}]\\ \ddot{\varphi}=\frac{1}{I_z}(l_fF_{yf}\cos(\delta_f)-l_rF_{yr}) \end{cases} \tag{16}⎩⎪⎨⎪⎧​v ˙y ​=−v x ​φ˙​+m 1 ​[F y f ​cos (δf ​)+F y r ​]φ¨​=I z ​1 ​(l f ​F y f ​cos (δf ​)−l r ​F y r ​)​(1 6 )

谢谢

Original: https://blog.csdn.net/qq_32618327/article/details/124353188
Author: 氢键H-H
Title: 车辆运动控制(2)车辆横摆动力学建模