常州电工培训

常州电工证

常州变频器培训

常州触摸屏培训

江苏和讯自动化设备有限公司欢迎您!
  • 和讯PLC,电工培训中心优势,名师团队一对一教学.
热门课程
联系方式
  • 常州和讯自动化培训中心
  • 常州市新北区太湖东路府琛大厦2号楼307-1室,307-2室(常州万达广场对面)
  • 电话:0519-85602926
  • 手机:15861139266 13401342299
当前位置:网站首页 > 新闻中心 新闻中心
基于Pytorch的从零开始的目标检测(下)-常州机器视觉学习,常州上位机学习
日期:2023-12-22 15:03:04人气:  标签:常州机器视觉学习 常州上位机学习

数据增强

数据增强是一种通过使用现有图像的不同变体创建新的训练图像来更好地概括我们的模型的技术。我们当前的训练集中只有 800 张图像,因此数据增强对于确保我们的模型不会过拟合非常重要。


对于这个问题,我使用了翻转、旋转、中心裁剪和随机裁剪。


这里唯一需要记住的是确保包围盒也以与图像相同的方式进行转换。


# modified from fast.ai

def crop(im, r, c, target_r, target_c): 

    return im[r:r+target_r, c:c+target_c]


# random crop to the original size

def random_crop(x, r_pix=8):

    """ Returns a random crop"""

    r, c,*_ = x.shape

    c_pix = round(r_pix*c/r)

    rand_r = random.uniform(0, 1)

    rand_c = random.uniform(0, 1)

    start_r = np.floor(2*rand_r*r_pix).astype(int)

    start_c = np.floor(2*rand_c*c_pix).astype(int)

    return crop(x, start_r, start_c, r-2*r_pix, c-2*c_pix)


def center_crop(x, r_pix=8):

    r, c,*_ = x.shape

    c_pix = round(r_pix*c/r)

    return crop(x, r_pix, c_pix, r-2*r_pix, c-2*c_pix)

def rotate_cv(im, deg, y=False, mode=cv2.BORDER_REFLECT, interpolation=cv2.INTER_AREA):

    """ Rotates an image by deg degrees"""

    r,c,*_ = im.shape

    M = cv2.getRotationMatrix2D((c/2,r/2),deg,1)

    if y:

        return cv2.warpAffine(im, M,(c,r), borderMode=cv2.BORDER_CONSTANT)

    return cv2.warpAffine(im,M,(c,r), borderMode=mode, flags=cv2.WARP_FILL_OUTLIERS+interpolation)


def random_cropXY(x, Y, r_pix=8):

    """ Returns a random crop"""

    r, c,*_ = x.shape

    c_pix = round(r_pix*c/r)

    rand_r = random.uniform(0, 1)

    rand_c = random.uniform(0, 1)

    start_r = np.floor(2*rand_r*r_pix).astype(int)

    start_c = np.floor(2*rand_c*c_pix).astype(int)

    xx = crop(x, start_r, start_c, r-2*r_pix, c-2*c_pix)

    YY = crop(Y, start_r, start_c, r-2*r_pix, c-2*c_pix)

    return xx, YY


def transformsXY(path, bb, transforms):

    x = cv2.imread(str(path)).astype(np.float32)

    x = cv2.cvtColor(x, cv2.COLOR_BGR2RGB)/255

    Y = create_mask(bb, x)

    if transforms:

        rdeg = (np.random.random()-.50)*20

        x = rotate_cv(x, rdeg)

        Y = rotate_cv(Y, rdeg, y=True)

        if np.random.random() > 0.5: 

            x = np.fliplr(x).copy()

            Y = np.fliplr(Y).copy()

        x, Y = random_cropXY(x, Y)

    else:

        x, Y = center_crop(x), center_crop(Y)

    return x, mask_to_bb(Y)

def create_corner_rect(bb, color='red'):

    bb = np.array(bb, dtype=np.float32)

    return plt.Rectangle((bb[1], bb[0]), bb[3]-bb[1], bb[2]-bb[0], color=color,

                         fill=False, lw=3)


def show_corner_bb(im, bb):

    plt.imshow(im)

    plt.gca().add_patch(create_corner_rect(bb))

4.png

PyTorch 数据集

现在我们已经有了数据增强,我们可以进行训练验证拆分并创建我们的 PyTorch 数据集。我们使用 ImageNet 统计数据对图像进行标准化,因为我们使用的是预训练的 ResNet 模型并在训练时在我们的数据集中应用数据增强。


X_train, X_val, y_train, y_val = train_test_split(X, Y, test_size=0.2, random_state=42)

def normalize(im):

    """Normalizes images with Imagenet stats."""

    imagenet_stats = np.array([[0.485, 0.456, 0.406], [0.229, 0.224, 0.225]])

    return (im - imagenet_stats[0])/imagenet_stats[1]

class RoadDataset(Dataset):

    def __init__(self, paths, bb, y, transforms=False):

        self.transforms = transforms

        self.paths = paths.values

        self.bb = bb.values

        self.y = y.values

    def __len__(self):

        return len(self.paths)

    

    def __getitem__(self, idx):

        path = self.paths[idx]

        y_class = self.y[idx]

        x, y_bb = transformsXY(path, self.bb[idx], self.transforms)

        x = normalize(x)

        x = np.rollaxis(x, 2)

        return x, y_class, y_bb

train_ds = RoadDataset(X_train['new_path'],X_train['new_bb'] ,y_train, transforms=True)

valid_ds = RoadDataset(X_val['new_path'],X_val['new_bb'],y_val)

batch_size = 64

train_dl = DataLoader(train_ds, batch_size=batch_size, shuffle=True)

valid_dl = DataLoader(valid_ds, batch_size=batch_size)


PyTorch 模型

对于这个模型,我使用了一个非常简单的预先训练的 resNet-34模型。由于我们有两个任务要完成,这里有两个最后的层: 包围盒回归器和图像分类器。


class BB_model(nn.Module):

    def __init__(self):

        super(BB_model, self).__init__()

        resnet = models.resnet34(pretrained=True)

        layers = list(resnet.children())[:8]

        self.features1 = nn.Sequential(*layers[:6])

        self.features2 = nn.Sequential(*layers[6:])

        self.classifier = nn.Sequential(nn.BatchNorm1d(512), nn.Linear(512, 4))

        self.bb = nn.Sequential(nn.BatchNorm1d(512), nn.Linear(512, 4))

        

    def forward(self, x):

        x = self.features1(x)

        x = self.features2(x)

        x = F.relu(x)

        x = nn.AdaptiveAvgPool2d((1,1))(x)

        x = x.view(x.shape[0], -1)

        return self.classifier(x), self.bb(x)


训练

对于损失,我们需要同时考虑分类损失和边界框回归损失,因此我们使用交叉熵和 L1 损失(真实值和预测坐标之间的所有绝对差之和)的组合。我已经将 L1 损失缩放了 1000 倍,因为分类和回归损失都在相似的范围内。除此之外,它是一个标准的 PyTorch 训练循环(使用 GPU):


def update_optimizer(optimizer, lr):

    for i, param_group in enumerate(optimizer.param_groups):

        param_group["lr"] = lr

def train_epocs(model, optimizer, train_dl, val_dl, epochs=10,C=1000):

    idx = 0

    for i in range(epochs):

        model.train()

        total = 0

        sum_loss = 0

        for x, y_class, y_bb in train_dl:

            batch = y_class.shape[0]

            x = x.cuda().float()

            y_class = y_class.cuda()

            y_bb = y_bb.cuda().float()

            out_class, out_bb = model(x)

            loss_class = F.cross_entropy(out_class, y_class, reduction="sum")

            loss_bb = F.l1_loss(out_bb, y_bb, reduction="none").sum(1)

            loss_bb = loss_bb.sum()

            loss = loss_class + loss_bb/C

            optimizer.zero_grad()

            loss.backward()

            optimizer.step()

            idx += 1

            total += batch

            sum_loss += loss.item()

        train_loss = sum_loss/total

        val_loss, val_acc = val_metrics(model, valid_dl, C)

        print("train_loss %.3f val_loss %.3f val_acc %.3f" % (train_loss, val_loss, val_acc))

    return sum_loss/total

def val_metrics(model, valid_dl, C=1000):

    model.eval()

    total = 0

    sum_loss = 0

    correct = 0 

    for x, y_class, y_bb in valid_dl:

        batch = y_class.shape[0]

        x = x.cuda().float()

        y_class = y_class.cuda()

        y_bb = y_bb.cuda().float()

        out_class, out_bb = model(x)

        loss_class = F.cross_entropy(out_class, y_class, reduction="sum")

        loss_bb = F.l1_loss(out_bb, y_bb, reduction="none").sum(1)

        loss_bb = loss_bb.sum()

        loss = loss_class + loss_bb/C

        _, pred = torch.max(out_class, 1)

        correct += pred.eq(y_class).sum().item()

        sum_loss += loss.item()

        total += batch

    return sum_loss/total, correct/total

model = BB_model().cuda()

parameters = filter(lambda p: p.requires_grad, model.parameters())

optimizer = torch.optim.Adam(parameters, lr=0.006)

train_epocs(model, optimizer, train_dl, valid_dl, epochs=15)


测试

现在我们已经完成了训练,我们可以选择一个随机图像并在上面测试我们的模型。尽管我们只有相当少量的训练图像,但是我们最终在测试图像上得到了一个相当不错的预测。


使用手机拍摄真实照片并测试模型将是一项有趣的练习。另一个有趣的实验是不执行任何数据增强并训练模型并比较两个模型。


# resizing test image

im = read_image('./road_signs/images_resized/road789.png')

im = cv2.resize(im, (int(1.49*300), 300))

cv2.imwrite('./road_signs/road_signs_test/road789.jpg', cv2.cvtColor(im, cv2.COLOR_RGB2BGR))

# test Dataset

test_ds = RoadDataset(pd.DataFrame([{'path':'./road_signs/road_signs_test/road789.jpg'}])['path'],pd.DataFrame([{'bb':np.array([0,0,0,0])}])['bb'],pd.DataFrame([{'y':[0]}])['y'])

x, y_class, y_bb = test_ds[0]

xx = torch.FloatTensor(x[None,])

xx.shape

# prediction

out_class, out_bb = model(xx.cuda())

out_class, out_bb

5.png


总结

现在我们已经介绍了目标检测的基本原理,并从头开始实现它,您可以将这些想法扩展到多对象情况,并尝试更复杂的模型,如 RCNN 和 YOLO!

本文网址:

相关信息:
版权所有 CopyRight 2006-2017 江苏和讯自动化设备有限公司 电话:0519-85602926 地址:常州市新北区太湖东路府琛大厦2号楼307-1室,307-2室
苏ICP备14016686号-2 技术支持:常州鹤翔网络
本站关键词:常州电工培训 常州电工证 常州变频器培训 常州触摸屏培训 网站地图 网站标签
在线与我们取得联系