使用PyTorch从0实现Fashion-MNIST数据集分类

news2024/11/24 23:05:27

        完整代码: 

from d2l import torch as d2l
import torch
from torchvision import transforms
from torchvision import datasets
from torch.utils.data import DataLoader
import matplotlib.pyplot as plt
from IPython import display


def get_fashion_mnist_labels(labels):  # @save
    """
    返回Fashion-MNIST数据集的文本标签.
    遍历labels,取出i,i是一个数字文本,通过int(i)转换成数字,然后作为索引,从text_labels获取类别名称.
    :param labels:  文本数字标签,labels中的数字是字符串,需要int()函数转换为整型数字.
    :return: 类别名称.
    Example:
        输入labels=['3','5']
        返回 ['dress','sandal']
    """
    text_labels = ['t-shirt', 'trouser', 'pullover', 'dress', 'coat',
                   'sandal', 'shirt', 'sneaker', 'bag', 'ankle boot']

    return [text_labels[int(i)] for i in labels]


def show_image_gray(mnist_train):
    figure = plt.figure(figsize=(8, 8))
    cols, rows = 3, 3
    for i in range(1, cols * rows + 1):
        sample_idx = torch.randint(len(mnist_train), size=(1,)).item()
        img, label = mnist_train[sample_idx]
        figure.add_subplot(rows, cols, i)
        plt.title(get_fashion_mnist_labels([label]))
        plt.axis("off")
        plt.imshow(img.squeeze(), cmap="gray")
    # plt.show()


def show_images_color(imgs, num_rows, num_cols, titles=None, scale=1.5):  # @save
    """
    绘制图像列表.
    :param imgs: 图像.
    :param num_rows: 行数.
    :param num_cols: 列数.
    :param titles: 标题.
    :param scale: 缩放比例.
    :return:
    """
    figsize = (num_cols * scale, num_rows * scale)
    _, axes = plt.subplots(num_rows, num_cols, figsize=figsize)
    axes = axes.flatten()
    for i, (ax, img) in enumerate(zip(axes, imgs)):
        if torch.is_tensor(img):
            # 图片张量
            ax.imshow(img.numpy())
        else:
            # PIL图片
            ax.imshow(img)
        ax.axes.get_xaxis().set_visible(False)
        ax.axes.get_yaxis().set_visible(False)
        if titles:
            ax.set_title(titles[i])
    # plt.show()


def softmax(X):
    """

    :param X:
    :return:

    Example:
        X=[[0, 1, 2]
          [3, 4, 5]]
        X_exp= [[e^0, e^1, e^2]
                [e^3, e^4, e^5]]
        partition=[[e^0+e^1+e^2]
                   [e^3+e^4+e^5]]
       X_exp / partition中对partition进行广播(按列复制)partition=
                                                [[e^0+e^1+e^2, e^0+e^1+e^2, e^0+e^1+e^2]
                                                 [e^3+e^4+e^5, e^3+e^4+e^5, e^3+e^4+e^5]]
       X_exp / partition = [[e^0/(e^0+e^1+e^2), e^1/(e^0+e^1+e^2), e^2/(e^0+e^1+e^2)]
                [e^3/(e^3+e^4+e^5), e^4/(e^3+e^4+e^5), e^5/(e^3+e^4+e^5)]]
    """
    X_exp = torch.exp(X)  # 矩阵的每个元素计算指数.
    partition = X_exp.sum(1, keepdim=True)  # 按行求和,保持张量阶数.
    return X_exp / partition  # 这里应用了广播机制 partition按列复制.


def accuracy_num(y_hat, y):  # @save
    """
    计算预测正确的数量
    :param y_hat: 预测值.
    :param y: 标签值.
    :return: 预测正确的个数.

    Example:
            y_hat = [[0.1, 0.2, 0.7]
                     [0.4, 0.3, 0.3]]
            y = [[2]
                 [1]]
            计算每行最大概率对应的索引,例如第一行最大概率为0.7,对应的索引为2,最终得到:
            y_hat = [[2]
                     [0]]
            然后将类型转换成y的数据类型,将预测值与y标签值判断是否相等,相等为True,否则为False,例如:
            cmp = [[True]
                   [False]]
            最后统计True的个数,返回1.
    """
    if len(y_hat.shape) > 1 and y_hat.shape[1] > 1:
        y_hat = y_hat.argmax(axis=1)
    cmp = (y_hat.type(y.dtype) == y)

    return float(cmp.type(y.dtype).sum())


def net(X):
    """
    神经网络
    :param X: 小批量输入数据,是一个张量,例如X张量维度[64, 1, 28, 28].
        第一个维度表示批量大小batch_size,第二个维度表示通道数,第三个维度表示图像高度,第四个维度表示图像宽度.
    :return: 输出.
    Example:
            神经网络线性变换:XW+b,例如:batch_size = 2
            神经网络结构:   输出层(2个神经元):  *  *
                           输入层(3个神经元):+   +   +
            X= [[0, 1, 2]
                [3, 4, 5]]
             W = [[1, 2]
                  [2, 0]
                  [1, 1]]
             b = [1, 2]
            XW = [[4, 2]
                  [16 11]]
            XW + b中b首先使用广播机制,按行复制得到
              b = [[1, 2]
                   [1, 2]]
            最终得到
            XW + b = [[5, 4]
                      [17, 13]]
    """
    # X张量维度[64, 1, 28, 28],W张量维度[784,10],W.shape[0]=784,
    # reshape表示将X变成两个维度,第二个维度为784,第一个维度自动计算,也就是64*1*28*28/784=64,
    # 所以reshape后X维度为[64,784],
    X = X.reshape((-1, W.shape[0]))

    X = torch.matmul(X, W) + b

    return softmax(X)


def evaluate_accuracy(net, data_iter):  # @save
    """
    计算在指定数据集上模型的精度.
    :param net: 神经网络对象.
    :param data_iter: 可迭代数据集.
    :return: 神经网络模型在数据集上的预测准确率.
    Example:

    """
    if isinstance(net, torch.nn.Module):
        net.eval()  # 将模型设置为评估模式
    # 初始化累加器.
    accumulator = Accumulator(2)  # [正确预测数,预测总数]
    with torch.no_grad():  # 这里不需要计算梯度,关闭梯度计算.
        for X, y in data_iter:  # 变量数据集. X的维度[64,1,28,28], y的维度[64,1]
            y_hat = net(X)  # 数据集输入神经网络,输出预测值y_hat.
            acc_num = accuracy_num(y_hat, y)  # 预测正确的个数.
            total = y.numel()  # 总数.
            accumulator.add(acc_num, total)  # 对每批数据集的正确个数,总数分别进行累加.

    return accumulator[0] / accumulator[1]


class Accumulator:  # @save
    """
    累加器:在n个变量上累加
    """

    def __init__(self, n):
        """
        初始化累加器.
        :param n: 累加器中的数据个数.
        Exapmle:
            n = 3, data = [0.0, 0.0, 0.0]
        """
        self.data = [0.0] * n  # 变成n个0.0的列表.

    def add(self, *args):
        """
        累加器对输入数据进行累加操作.
        :param args:
        :return:
        Example:
            如果data=[0.0,0.0],args=[2,64],
            zip对两个列表的对应位置压缩变成元组列表:[(0.0,2),(0.0,64)],
            遍历元组列表,每个元组中两个元素求和得到data=[2.0,64.0].
        """

        self.data = [a + float(s) for a, s in zip(self.data, args)]

    def reset(self):
        """
        重置累加器.
        :return: 重置后的累加器.
        Example:
            data = [1, 4, 5],重置后data = [0.0, 0.0, 0.0]
        """
        self.data = [0.0] * len(self.data)

    def __getitem__(self, idx):
        """
        根据索引获取累加器对应索引上的值.
        :param idx: 索引
        :return: 数据索引上的值.
        Example:
            data = [1, 4, 5], idx=1
            data[idx] = 4.
        """
        return self.data[idx]


def cross_entropy(y_hat, y):
    """
    计算每个样本的交叉熵损失值函数,存储在一个列表中.
    后面在计算交叉熵总损失 = 所有样本交叉熵损失值的和.
    :param y_hat: 预测值,是一个(batch_size,label_num)的二阶张量,第一个维度是批次数,第二个维度是类别数,例如(64,10).
    :param y: 标签值,是一个(batch_size)的一阶张量.
    :return: 交叉熵损失值.
    Example:
        假设批次数batch_size=2, 类别总数label_num=3.
        y_hat = [[0.1, 0.3, 0.6]
                 [0.3, 0.5, 0.2]]
        y_hat的每一行表示一个输入样本,输出以后,对应每个类别的概率.
        y = [[0]
             [2]]
        y_hat[range(len(y_hat)), y]表示从y_hat中按照行索引和列索引取值.
        行索引range(len(y_hat)) = [0,1], 列索引 y=[0,2] ,按照行列索引组成(0,0)和(1,2)。
        从y_hat中取出位置为(0,0)和(1,2)的值,得到prob = [0.1,0.2],
        最后对prob的每个值取对数的负数,得到[-log0.1, -log0.2],它的每个值表示每个样本的损失值.
        例如第一个样本的损失值为-log0.1,所有样本的总损失值可以如下计算:
        -log0.1-log0.2
    """
    prob = y_hat[range(len(y_hat)), y]

    return - torch.log(prob)


def updater(batch_size, lr=0.1):
    """
    更新参数.
    with torch.no_grad()是一个用于禁用梯度的上下文管理器。禁用梯度计算对于推理是很有用的,当我们确定不会调用Tensor.backward()时,
    它将减少计算的内存消耗。因为在此模式下,即使输入为 requires_grad=True,每次计算的结果也将具有requires_grad=False。
    总的来说, with torch.no_grad() 可以理解为,在管理器外产生的与原参数有关联的参数requires_grad属性都默认为True,
    而在该管理器内新产生的参数的requires_grad属性都将置为False。
    :param batch_size: 批次大小
    :param lr: 学习率,是一个超参数,默认值为0.1.
    Example:
        假设损失函数loss(W,b)=2w_1^2+3w_2^3+b,对W的梯度向量为(4w_1,9w_2),
        假设W的初始值为 W = [0.1,0.3],lr = 0.1,batch_size = 2,第一次更新:
        W = W - (lr / batch_size) * grad
        W = [0.1,0.3]-(0.1 / 2) * [0.4, 2.7] = [0.1,0.3] - [0.02, 0.135] = [0.08, 0.865]
        b同理.
    """
    with torch.no_grad():
        for param in [W, b]:
            param -= lr * param.grad / batch_size
            param.grad.zero_()  # 梯度清零.


def train_one_epoch(net, train_iter, loss, updater):  # @save
    """
    一轮训练.
    :param net: 神经网路模型.
    :param train_iter: 训练数据集.
    :param loss: 损失函数.
    :param updater: 更新器.
    :return:
    """
    # 将模型设置为训练模式
    if isinstance(net, torch.nn.Module):
        net.train()
    # 训练损失总和、训练准确度总和、样本数
    accumulator = Accumulator(3)  # [0.0, 0.0, 0.0],第一个表示总损失值,第二个表示预测准确个数,第三个表示样本总数.
    for X, y in train_iter:  # 遍历数据集.
        y_hat = net(X)  # 正向传播,计算最终输出.
        loss_value = loss(y_hat, y)  # 计算损失.
        if isinstance(updater, torch.optim.Optimizer):
            # 使用PyTorch内置的优化器和损失函数
            updater.zero_grad()
            loss_value.mean().backward()
            updater.step()
        else:
            # 使用定制的优化器和损失函数
            loss_value.sum().backward()  # loss_value.sum() 计算总损失,然后反向传播,计算梯度.
            updater(X.shape[0])  # 更新参数.
        # 对每批数据集的损失值、预测准确个数、样本数进行累加.
        accumulator.add(float(loss_value.sum()), accuracy_num(y_hat, y), y.numel())
    # 返回训练损失和训练精度

    return accumulator[0] / accumulator[2], accumulator[1] / accumulator[2]


class Animator:  # @save
    """在动画中绘制数据"""

    def __init__(self, xlabel=None, ylabel=None, legend=None, xlim=None,
                 ylim=None, xscale='linear', yscale='linear',
                 fmts=('-', 'm--', 'g-.', 'r:'), nrows=1, ncols=1,
                 figsize=(3.5, 2.5)):
        # 增量地绘制多条线
        if legend is None:
            legend = []
        # d2l.use_svg_display()
        self.fig, self.axes = plt.subplots(nrows, ncols, figsize=figsize)
        if nrows * ncols == 1:
            self.axes = [self.axes, ]
        # 使用lambda函数捕获参数
        self.config_axes = lambda: d2l.set_axes(
            self.axes[0], xlabel, ylabel, xlim, ylim, xscale, yscale, legend)
        self.X, self.Y, self.fmts = None, None, fmts

    def add(self, x, y):
        # 向图表中添加多个数据点
        if not hasattr(y, "__len__"):
            y = [y]
        n = len(y)
        if not hasattr(x, "__len__"):
            x = [x] * n
        if not self.X:
            self.X = [[] for _ in range(n)]
        if not self.Y:
            self.Y = [[] for _ in range(n)]
        for i, (a, b) in enumerate(zip(x, y)):
            if a is not None and b is not None:
                self.X[i].append(a)
                self.Y[i].append(b)
        self.axes[0].cla()
        for x, y, fmt in zip(self.X, self.Y, self.fmts):
            self.axes[0].plot(x, y, fmt)
        self.config_axes()
        display.display(self.fig)
        display.clear_output(wait=True)


def train(net, train_iter, test_iter, loss, num_epochs, updater):  # @save
    """
    训练神经网络模型.
    :param net: 神经网络.
    :param train_iter: 训练数据集.
    :param test_iter: 测试数据集.
    :param loss: 损失.
    :param num_epochs: 训练轮次.
    :param updater: 参数更新器.
    :return:
    """

    animator = Animator(xlabel='epoch', xlim=[1, num_epochs], ylim=[0.3, 0.9],
                        legend=['train loss', 'train acc', 'test acc'])
    train_metrics = 0.0, 0
    test_acc = 0
    for epoch in range(num_epochs):  # 遍历轮次.
        train_metrics = train_one_epoch(net, train_iter, loss, updater)  # 训练一轮,并返回平均损失和准确率.
        test_acc = evaluate_accuracy(net, test_iter)  # 使用训练的模型测量在测试集上的准确度.
        animator.add(epoch + 1, train_metrics + (test_acc,))
    plt.show()

    train_loss, train_acc = train_metrics
    # 断言:如果不满足断言,程序中断.
    assert train_loss < 0.5, train_loss  # 断言总损失需要小于0.5.
    assert 1 >= train_acc > 0.7, train_acc  # 断案训练集的准确率需要在(0.7,1]之间.
    assert 1 >= test_acc > 0.7, test_acc  # 断案训练集的准确率需要在(0.7,1]之间.


def predict(net, test_iter, n=6):  # @save
    """预测标签(定义见第3章)"""
    for X, y in test_iter:
        break
    trues = get_fashion_mnist_labels(y)
    preds = get_fashion_mnist_labels(net(X).argmax(axis=1))
    titles = [true + '\n' + pred for true, pred in zip(trues, preds)]
    d2l.show_images(
        X[0:n].reshape((n, 28, 28)), 1, n, titles=titles[0:n])
    plt.show()


if __name__ == '__main__':
    torch.manual_seed(42)
    trans = transforms.ToTensor()

    # 将数据集下载到data目录
    mnist_train = datasets.FashionMNIST(
        root="data",
        train=True,
        download=True,
        transform=trans
    )
    mnist_test = datasets.FashionMNIST(
        root="data",
        train=False,
        download=True,
        transform=trans
    )

    train_size, test_size = len(mnist_train), len(mnist_test)
    print('train_size=', train_size, 'test_size=', test_size)

    # mnist_train[0] 表示第一行训练数据,包含图像和标签,是两者组成的一个二元组.
    # mnist_train[0][0]表示第一个图像,是一个三阶张量,第一阶表示通道数,第二阶表示图像高度,第三阶表示图像宽度,(1,28,28)。
    # mnist_train[0][1]表示第一个图像的标签.
    print('mnist_train.shape=', mnist_train[0][0].shape)
    print('mnist_train.label=', mnist_train[0][1])

    # 可视化图像.
    show_image_gray(mnist_train)

    batch_size = 64
    dataloader_workers = 4
    # 如果代码不写在main中,num_workers只能设置为0,否则报错。与Windows系统有关。
    # https://blog.csdn.net/weixin_45953673/article/details/132417457
    train_iter = DataLoader(mnist_train, batch_size=batch_size, shuffle=True, num_workers=dataloader_workers)
    test_iter = DataLoader(mnist_test, batch_size=batch_size, shuffle=True, num_workers=dataloader_workers)
    # iter()将DataLoader返回转换成一个可迭代对象,类似可迭代对象list.
    # next()对可迭代对象进行迭代,类似遍历list.
    train_features, train_labels = next(iter(train_iter))
    # 四阶张量,torch.Size([64, 1, 28, 28]).
    print('train_features.shape=', train_features.shape)
    show_images_color(imgs=train_features.reshape(batch_size, 28, 28),
                      num_rows=2, num_cols=9,
                      titles=get_fashion_mnist_labels(train_labels))
    # 初始化权重.
    num_inputs = 784
    num_outputs = 10
    W = torch.normal(0, 0.01, size=(num_inputs, num_outputs), requires_grad=True)
    b = torch.zeros(num_outputs, requires_grad=True)

    accuracy = evaluate_accuracy(net, test_iter)
    print('accuracy=', accuracy)

    num_epochs = 10
    train(net, train_iter, test_iter, cross_entropy, num_epochs, updater)

    predict(net, test_iter)

程序输出结果:

train_size= 60000 test_size= 10000
mnist_train.shape= torch.Size([1, 28, 28])
mnist_train.label= 9
train_features.shape= torch.Size([64, 1, 28, 28])
accuracy= 0.0484
Figure(350x250)
Figure(350x250)
Figure(350x250)
Figure(350x250)
Figure(350x250)
Figure(350x250)
Figure(350x250)
Figure(350x250)
Figure(350x250)
Figure(350x250)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2208957.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

BBR 的不公平性

BBR 公平收敛在相图中的细节 和 aimd&#xff0c;bbr&#xff0c;inflt 守恒的收敛相图总结 已经介绍了 BBR 的 gain 不公平性&#xff0c;本文介绍 BBR 的 RTT 不公平性。 直觉上&#xff0c;BBR 采用 probe_quota gain * maxbw * minrtt 来 probe 带宽&#xff0c;minrtt 越…

掌握Postman,开启API测试新纪元!

Postman是一款流行的API测试工具和开发环境&#xff0c;旨在简化API开发过程、测试和文档编制。它提供了一套功能强大的工具&#xff0c;帮助开发人员更轻松地构建、测试和调试Web服务。 Postman 工具的优势 Postman 可以快速构建请求、还可以保存以后再使用。 Postman 还提…

改进系列:TransUnet结合SAM box改进对MICCAI FLARE腹部13器官图像分割

目录 1、前言 2、实现思路 3、实验代码 3.1 环境配置 3.2 数据集 3.3 训练 3.4 指标 3.5 推理 4、其他 1、前言 本章尝试将TransUnet和SAM结合&#xff0c;以期望达到更换的模型 TransUnet作为医学图像分割的基准&#xff0c;在许多数据集上均取得了很好的效果&#x…

JavaSE——认识异常

1.概念 在生活中&#xff0c;人有时会生病&#xff0c;在程序中也是一样&#xff0c;程序猿是一帮办事严谨、追求完美的高科技人才。在日常开发中&#xff0c;绞尽脑汁将代码写的尽善尽美&#xff0c;在程序运行过程中&#xff0c;难免会出现一些奇奇怪怪的问题。有时通过代码很…

2024/10/12 计组大题专训

2018&#xff1a; 2019&#xff1a; 2020&#xff1a; 2021&#xff1a;

【多线程】多线程(12):多线程环境下使用哈希表

【多线程环境下使用哈希表&#xff08;重点掌握&#xff09;】 可以使用类&#xff1a;“ConcurrentHashMap” ★ConcurrentHashMap对比HashMap和Hashtable的优化点 1.优化了锁的粒度【最核心】 //Hashtable的加锁&#xff0c;就是直接给put&#xff0c;get等方法加上synch…

AI+若依框架day02

项目实战 项目介绍 帝可得是什么 角色和功能 页面原型 库表设计 初始AI AIGC 提示工程 Prompt的组成 Prompt练习 项目搭建 点位管理 需求说明 库表设计

多线程学习篇四:synchronized

1. synchronized 的使用 1.1 作用于实例方法 Slf4j(topic "c.Test01") public class Test01 {public synchronized void method1() {// 代码逻辑} } 等价于下列写法&#xff1a; Slf4j(topic "c.Test01") public class Test01 {public void method1…

基于机器学习的虚假新闻智能检测系统

温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长 QQ 名片 :) 1. 项目简介 随着互联网的普及和社交媒体的发展&#xff0c;虚假新闻&#xff08;fake news&#xff09;问题日益严重&#xff0c;对社会和个人产生了诸多负面影响。传统的新闻审核方法通常依赖于人工审核&…

基于gewechat制作第一个微信聊天机器人

Gewe 个微框架 GeWe&#xff08;个微框架&#xff09;是一个创新性的软件开发框架&#xff0c;为个人微信号以及企业信息安全提供了强大的功能和保障。GeWe的设计旨在简化开发过程&#xff0c;使开发者能够高效、灵活地构建和定制通信协议&#xff0c;以满足不同应用场景的需求…

SSL---SSL certificate problem

0 Preface/Foreword 0.1 SSL certificate problem 开发过程中&#xff0c;gitlab-runner连接gitlab时候出现SSL 证书问题。 场景&#xff1a;公司的gitlab runner服务器引入了SSL证书&#xff0c;每年都会主动更新一次。当前的gitlab-runner运行在PC机器上&#xff0c;但是g…

ZYNQ使用XGPIO驱动外设模块(前半部分)

目录 目录 一、新建BD文档&#xff0c;添加ZYNQ处理器 1.BD文档: 2.在Vivado中&#xff0c;BD文件的生成过程通常包括以下步骤&#xff1a; 1)什么是Tcl Console: 3.PL部分是FPGA可编程逻辑部分&#xff0c;它提供了丰富的IO资源&#xff0c;可以用于实现各种硬件接口和功…

刘文超数量关系笔记

第一章解题技巧 第一节代入排除法 代入排除是数量关系第一大法。 代入排除顾名思义是将答案选项代入原题目&#xff0c;与题意不符的选项即可排除&#xff0c; 最终得出正确答案。 优先使用代入排除的题型&#xff1a; &#xff08;1&#xff09;多位数问题、余数问题、年龄…

node.js服务器基础

node.js的事件循环 node.js是基于事件驱动的&#xff0c;通常在代码中注册想要等待的事件&#xff0c;设定好回调函数&#xff0c;当事件触发的时候就会调用回调函数。如果node.js没有要处理的事件了&#xff0c;那整个就结束了;事件里面可以继续插入事件&#xff0c;如果有事…

【2021】知识图谱导论(陈华钧)——阅读思考与笔记

tips&#xff1a;其中所有【】表示的内容为博主本人想法&#xff0c;非作者观点&#xff0c;请注意辨别。 这是一本全面覆盖知识图谱多个方面的书籍。书中不仅详细介绍了知识图谱的表示、存储、获取、推理、融合、问答和分析等七大方面&#xff0c;还深入探讨了多模态知识图谱…

【Nginx系列】Nginx启动失败

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

[⑦5G NR]: PSS/SSS同步信号学习

在5G中&#xff0c;PSS(Primary Synchronization Signal) 主同步信号和SSS(Secondary Synchronization Signal)辅同步信号是用于物理层的信号&#xff0c;用于小区的搜索。 PSS 跟据协议38.211 7.4.2.2章节&#xff0c;PSS是3条长度为127的m序列&#xff0c;分别对应 N I D (…

空间解析几何4-空间中线段到圆的距离【附MATLAB代码】

目录 理论公式 matlab代码 理论公式 对于解一元4次方程&#xff0c;请详见我的博客 一元四次方程求解 -【附MATLAB代码】-CSDN博客文章浏览阅读1.4k次&#xff0c;点赞41次&#xff0c;收藏4次。最近在研究机器人的干涉&#xff08;碰撞&#xff09;检测&#xff0c;遇到了一…

义堂镇韦家巷村第十六届老人节暨孝善互助基金启动仪式成功举行

金秋十月爽&#xff0c;浓浓敬老情。10月11日晚&#xff0c;以“孝善韦家巷情暖重阳节”为主题的兰山区义堂镇韦家巷村第十六届老人节暨韦家巷村孝善互助基金启动仪式在韦家巷村文化广场盛大举行。 山东省民间文艺家协会副主席、临沂市民间文艺家协会主席、临沂市文联办公室主…

使用git页面如何用旧项目创建一个新项目出来并且保留所有分支内容和提交历史

使用git页面如何用旧项目创建一个新项目出来并且保留所有分支内容和提交历史 1、点击创建项目 2、点击导入项目