【深度学习】Chinese-CLIP 使用教程,图文检索,跨模态检索,零样本图片分类

代码:https://github.com/OFA-Sys/Chinese-CLIP/blob/master/deployment.md

文章目录

  • 安装环境和onnx推理
  • 转换所有模型为onnx
  • 测试所有onnx模型的脚本
  • onnx cpu方式执行
  • docker镜像

安装环境和onnx推理

安装环境,下载权重放置到指定目录,进行onnx转换:

conda create -n clip_cn python=3.8 -y
conda activate clip_cn
conda install pytorch==1.12.1 torchvision==0.13.1 torchaudio==0.12.1 cudatoolkit=11.6 -c pytorch -c conda-forge -y
conda install numpy tqdm six -y
conda install -c conda-forge timm -y
conda install -c anaconda lmdb==1.3.0 -ycd Chinese-CLIP
pip install -e .
mkdir DATAPATH
export CUDA_VISIBLE_DEVICES=0
export PYTHONPATH=${PYTHONPATH}:`pwd`/cn_clip
export DATAPATH="./DATAPATH"
checkpoint_path=${DATAPATH}/pretrained_weights/clip_cn_vit-b-16.pt # 指定要转换的ckpt完整路径
echo checkpoint_path
echo $checkpoint_path
mkdir -p ${DATAPATH}/deploy/ # 创建ONNX模型的输出文件夹
pip install onnx onnxruntime-gpu onnxmltools
python cn_clip/deploy/pytorch_to_onnx.py \--model-arch ViT-B-16 \--pytorch-ckpt-path ${checkpoint_path} \--save-onnx-path ${DATAPATH}/deploy/vit-b-16 \--convert-text --convert-vision

转换成功后:

Finished PyTorch to ONNX conversion...
>>> The text FP32 ONNX model is saved at ./DATAPATH/deploy/vit-b-16.txt.fp32.onnx
>>> The text FP16 ONNX model is saved at ./DATAPATH/deploy/vit-b-16.txt.fp16.onnx with extra file ./DATAPATH/deploy/vit-b-16.txt.fp16.onnx.extra_file
>>> The vision FP32 ONNX model is saved at ./DATAPATH/deploy/vit-b-16.img.fp32.onnx
>>> The vision FP16 ONNX model is saved at ./DATAPATH/deploy/vit-b-16.img.fp16.onnx with extra file ./DATAPATH/deploy/vit-b-16.img.fp16.onnx.extra_file

可以看到转换好的文件:
在这里插入图片描述

测试是否转换成功的python代码:

# 完成必要的import(下文省略)
import onnxruntime
from PIL import Image
import numpy as np
import torch
import argparse
import cn_clip.clip as clip
from clip import load_from_name, available_models
from clip.utils import _MODELS, _MODEL_INFO, _download, available_models, create_model, image_transform# 载入ONNX图像侧模型(**请替换${DATAPATH}为实际的路径**)
img_sess_options = onnxruntime.SessionOptions()
img_run_options = onnxruntime.RunOptions()
img_run_options.log_severity_level = 2
img_onnx_model_path="./DATAPATH/deploy/vit-b-16.img.fp16.onnx"
img_session = onnxruntime.InferenceSession(img_onnx_model_path,sess_options=img_sess_options,providers=["CUDAExecutionProvider"])# 预处理图片
model_arch = "ViT-B-16" # 这里我们使用的是ViT-B-16规模,其他规模请对应修改
preprocess = image_transform(_MODEL_INFO[model_arch]['input_resolution'])
# 示例皮卡丘图片,预处理后得到[1, 3, 分辨率, 分辨率]尺寸的Torch Tensor
image = preprocess(Image.open("examples/pokemon.jpeg")).unsqueeze(0)# 用ONNX模型计算图像侧特征
image_features = img_session.run(["unnorm_image_features"], {"image": image.cpu().numpy()})[0] # 未归一化的图像特征
image_features = torch.tensor(image_features)
image_features /= image_features.norm(dim=-1, keepdim=True) # 归一化后的Chinese-CLIP图像特征,用于下游任务
print(image_features.shape) # Torch Tensor shape: [1, 特征向量维度]# 载入ONNX文本侧模型(**请替换${DATAPATH}为实际的路径**)
txt_sess_options = onnxruntime.SessionOptions()
txt_run_options = onnxruntime.RunOptions()
txt_run_options.log_severity_level = 2
txt_onnx_model_path="./DATAPATH/deploy/vit-b-16.txt.fp16.onnx"
txt_session = onnxruntime.InferenceSession(txt_onnx_model_path,sess_options=txt_sess_options,providers=["CUDAExecutionProvider"])# 为4条输入文本进行分词。序列长度指定为52,需要和转换ONNX模型时保持一致(参见转换时的context-length参数)
text = clip.tokenize(["杰尼龟", "妙蛙种子", "小火龙", "皮卡丘"], context_length=52)# 用ONNX模型依次计算文本侧特征
text_features = []
for i in range(len(text)):one_text = np.expand_dims(text[i].cpu().numpy(),axis=0)text_feature = txt_session.run(["unnorm_text_features"], {"text":one_text})[0] # 未归一化的文本特征text_feature = torch.tensor(text_feature)text_features.append(text_feature)
text_features = torch.squeeze(torch.stack(text_features),dim=1) # 4个特征向量stack到一起
text_features = text_features / text_features.norm(dim=1, keepdim=True) # 归一化后的Chinese-CLIP文本特征,用于下游任务
print(text_features.shape) # Torch Tensor shape: [4, 特征向量维度]# 内积后softmax
# 注意在内积计算时,由于对比学习训练时有temperature的概念
# 需要乘上模型logit_scale.exp(),我们的预训练模型logit_scale均为4.6052,所以这里乘以100
# 对于用户自己的ckpt,请使用torch.load载入后,查看ckpt['state_dict']['module.logit_scale']或ckpt['state_dict']['logit_scale']
logits_per_image = 100 * image_features @ text_features.t()
print(logits_per_image.softmax(dim=-1)) # 图文相似概率: [[1.2252e-03, 5.2874e-02, 6.7116e-04, 9.4523e-01]]

转换所有模型为onnx

在这里插入图片描述

# ["RN50", "ViT-B-16", "ViT-L-14", "ViT-L-14-336", "ViT-H-14"]
checkpoint_path=${DATAPATH}/pretrained_weights/clip_cn_rn50.pt
python cn_clip/deploy/pytorch_to_onnx.py \--model-arch RN50 \--pytorch-ckpt-path ${checkpoint_path} \--save-onnx-path ${DATAPATH}/deploy/RN50 \--convert-text --convert-visioncheckpoint_path=${DATAPATH}/pretrained_weights/clip_cn_vit-b-16.pt
python cn_clip/deploy/pytorch_to_onnx.py \--model-arch ViT-B-16 \--pytorch-ckpt-path ${checkpoint_path} \--save-onnx-path ${DATAPATH}/deploy/ViT-B-16 \--convert-text --convert-visioncheckpoint_path=${DATAPATH}/pretrained_weights/clip_cn_vit-l-14.pt
python cn_clip/deploy/pytorch_to_onnx.py \--model-arch "ViT-L-14"\--pytorch-ckpt-path ${checkpoint_path} \--save-onnx-path ${DATAPATH}/deploy/ViT-L-14 \--convert-text --convert-visioncheckpoint_path=${DATAPATH}/pretrained_weights/clip_cn_vit-l-14-336.pt
python cn_clip/deploy/pytorch_to_onnx.py \--model-arch "ViT-L-14-336"\--pytorch-ckpt-path ${checkpoint_path} \--save-onnx-path ${DATAPATH}/deploy/ViT-L-14-336 \--convert-text --convert-visioncheckpoint_path=${DATAPATH}/pretrained_weights/clip_cn_vit-h-14.pt
python cn_clip/deploy/pytorch_to_onnx.py \--model-arch "ViT-H-14"\--pytorch-ckpt-path ${checkpoint_path} \--save-onnx-path ${DATAPATH}/deploy/ViT-H-14 \--convert-text --convert-vision

测试所有onnx模型的脚本

# 完成必要的import(下文省略)
import onnxruntime
from PIL import Image
import numpy as np
import torch
import argparse
import cn_clip.clip as clip
from clip import load_from_name, available_models
from clip.utils import _MODELS, _MODEL_INFO, _download, available_models, create_model, image_transformmodel_list=["RN50", "ViT-B-16", "ViT-L-14", "ViT-L-14-336", "ViT-H-14"]for model_name in model_list:print(model_name)# 载入ONNX图像侧模型(**请替换${DATAPATH}为实际的路径**)img_sess_options = onnxruntime.SessionOptions()img_run_options = onnxruntime.RunOptions()img_run_options.log_severity_level = 2img_onnx_model_path=f"./DATAPATH/deploy/{model_name}.img.fp16.onnx"img_session = onnxruntime.InferenceSession(img_onnx_model_path,sess_options=img_sess_options,providers=["CUDAExecutionProvider"])# 预处理图片model_arch = model_name # 这里我们使用的是ViT-B-16规模,其他规模请对应修改preprocess = image_transform(_MODEL_INFO[model_arch]['input_resolution'])# 示例皮卡丘图片,预处理后得到[1, 3, 分辨率, 分辨率]尺寸的Torch Tensorimage = preprocess(Image.open("examples/pokemon.jpeg")).unsqueeze(0)# 用ONNX模型计算图像侧特征image_features = img_session.run(["unnorm_image_features"], {"image": image.cpu().numpy()})[0] # 未归一化的图像特征image_features = torch.tensor(image_features)image_features /= image_features.norm(dim=-1, keepdim=True) # 归一化后的Chinese-CLIP图像特征,用于下游任务print(image_features.shape) # Torch Tensor shape: [1, 特征向量维度]# 载入ONNX文本侧模型(**请替换${DATAPATH}为实际的路径**)txt_sess_options = onnxruntime.SessionOptions()txt_run_options = onnxruntime.RunOptions()txt_run_options.log_severity_level = 2txt_onnx_model_path=f"./DATAPATH/deploy/{model_name}.txt.fp16.onnx"txt_session = onnxruntime.InferenceSession(txt_onnx_model_path,sess_options=txt_sess_options,providers=["CUDAExecutionProvider"])# 为4条输入文本进行分词。序列长度指定为52,需要和转换ONNX模型时保持一致(参见转换时的context-length参数)text = clip.tokenize(["杰尼龟", "妙蛙种子", "小火龙", "皮卡丘"], context_length=52)# 用ONNX模型依次计算文本侧特征text_features = []for i in range(len(text)):one_text = np.expand_dims(text[i].cpu().numpy(),axis=0)text_feature = txt_session.run(["unnorm_text_features"], {"text":one_text})[0] # 未归一化的文本特征text_feature = torch.tensor(text_feature)text_features.append(text_feature)text_features = torch.squeeze(torch.stack(text_features),dim=1) # 4个特征向量stack到一起text_features = text_features / text_features.norm(dim=1, keepdim=True) # 归一化后的Chinese-CLIP文本特征,用于下游任务print(text_features.shape) # Torch Tensor shape: [4, 特征向量维度]# 内积后softmax# 注意在内积计算时,由于对比学习训练时有temperature的概念# 需要乘上模型logit_scale.exp(),我们的预训练模型logit_scale均为4.6052,所以这里乘以100# 对于用户自己的ckpt,请使用torch.load载入后,查看ckpt['state_dict']['module.logit_scale']或ckpt['state_dict']['logit_scale']logits_per_image = 100 * image_features @ text_features.t()print(logits_per_image.softmax(dim=-1)) # 图文相似概率: [[1.2252e-03, 5.2874e-02, 6.7116e-04, 9.4523e-01]]

onnx cpu方式执行

环境:

conda create -n clip_cn_cpu python=3.8 -y
conda activate clip_cn_cpu
conda install pytorch==1.12.1 torchvision==0.13.1 torchaudio==0.12.1 cpuonly -c pytorch -y
conda install numpy tqdm six -y
conda install -c conda-forge timm -y
conda install -c anaconda lmdb==1.3.0 -y
cd Chinese-CLIP
pip install -e .
export PYTHONPATH=${PYTHONPATH}:`pwd`/cn_clip
pip install onnx onnxruntime onnxmltools

测试脚本:

# 完成必要的import(下文省略)
import onnxruntime
from PIL import Image
import numpy as np
import torch
import argparse
import cn_clip.clip as clip
from clip import load_from_name, available_models
from clip.utils import _MODELS, _MODEL_INFO, _download, available_models, create_model, image_transformmodel_list=["RN50", "ViT-B-16", "ViT-L-14", "ViT-L-14-336", "ViT-H-14"]for model_name in model_list:print(model_name)# 载入ONNX图像侧模型(**请替换${DATAPATH}为实际的路径**)img_sess_options = onnxruntime.SessionOptions()img_run_options = onnxruntime.RunOptions()img_run_options.log_severity_level = 2img_onnx_model_path=f"./DATAPATH/deploy/{model_name}.img.fp16.onnx"img_session = onnxruntime.InferenceSession(img_onnx_model_path,sess_options=img_sess_options,providers=["CPUExecutionProvider"])# 预处理图片model_arch = model_name # 这里我们使用的是ViT-B-16规模,其他规模请对应修改preprocess = image_transform(_MODEL_INFO[model_arch]['input_resolution'])# 示例皮卡丘图片,预处理后得到[1, 3, 分辨率, 分辨率]尺寸的Torch Tensorimage = preprocess(Image.open("examples/pokemon.jpeg")).unsqueeze(0)# 用ONNX模型计算图像侧特征image_features = img_session.run(["unnorm_image_features"], {"image": image.cpu().numpy()})[0] # 未归一化的图像特征image_features = torch.tensor(image_features)image_features /= image_features.norm(dim=-1, keepdim=True) # 归一化后的Chinese-CLIP图像特征,用于下游任务print(image_features.shape) # Torch Tensor shape: [1, 特征向量维度]# 载入ONNX文本侧模型(**请替换${DATAPATH}为实际的路径**)txt_sess_options = onnxruntime.SessionOptions()txt_run_options = onnxruntime.RunOptions()txt_run_options.log_severity_level = 2txt_onnx_model_path=f"./DATAPATH/deploy/{model_name}.txt.fp16.onnx"txt_session = onnxruntime.InferenceSession(txt_onnx_model_path,sess_options=txt_sess_options,providers=["CPUExecutionProvider"])# 为4条输入文本进行分词。序列长度指定为52,需要和转换ONNX模型时保持一致(参见转换时的context-length参数)text = clip.tokenize(["杰尼龟", "妙蛙种子", "小火龙", "皮卡丘"], context_length=52)# 用ONNX模型依次计算文本侧特征text_features = []for i in range(len(text)):one_text = np.expand_dims(text[i].cpu().numpy(),axis=0)text_feature = txt_session.run(["unnorm_text_features"], {"text":one_text})[0] # 未归一化的文本特征text_feature = torch.tensor(text_feature)text_features.append(text_feature)text_features = torch.squeeze(torch.stack(text_features),dim=1) # 4个特征向量stack到一起text_features = text_features / text_features.norm(dim=1, keepdim=True) # 归一化后的Chinese-CLIP文本特征,用于下游任务print(text_features.shape) # Torch Tensor shape: [4, 特征向量维度]# 内积后softmax# 注意在内积计算时,由于对比学习训练时有temperature的概念# 需要乘上模型logit_scale.exp(),我们的预训练模型logit_scale均为4.6052,所以这里乘以100# 对于用户自己的ckpt,请使用torch.load载入后,查看ckpt['state_dict']['module.logit_scale']或ckpt['state_dict']['logit_scale']logits_per_image = 100 * image_features @ text_features.t()print(logits_per_image.softmax(dim=-1)) # 图文相似概率: [[1.2252e-03, 5.2874e-02, 6.7116e-04, 9.4523e-01]]

docker镜像

dockerfile

FROM ubuntu:20.04
RUN apt-get update && apt-get install -y wget curl
RUN wget http://mirrors.tuna.tsinghua.edu.cn/anaconda/miniconda/Miniconda3-latest-Linux-x86_64.sh -O /tmp/conda.sh && \bash /tmp/conda.sh -b && rm /tmp/conda.sh
WORKDIR /Chinese-CLIP/
COPY ./ /Chinese-CLIP/
RUN cp ./deps/.condarc /root/
ENV DEBIAN_FRONTEND=noninteractive
# 安装 tzdata 包并设置时区为上海(无交互)
RUN apt-get update && \apt-get install -y tzdata && \ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && \echo "Asia/Shanghai" > /etc/timezone
RUN ~/miniconda3/bin/conda init bash && . ~/.bashrcRUN apt-get install -y python3-pipRUN . ~/.bashrc && pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simpleRUN . ~/.bashrc && conda create -n clip_cn_cpu python=3.8 -y &&  \conda activate clip_cn_cpu &&  \conda install pytorch==1.12.1 torchvision==0.13.1 torchaudio==0.12.1 cpuonly -c pytorch -y
RUN . ~/.bashrc && conda activate clip_cn_cpu && conda install numpy tqdm six -y
RUN . ~/.bashrc && conda activate clip_cn_cpu && conda install -c conda-forge timm -y
RUN . ~/.bashrc && conda activate clip_cn_cpu && pip install  lmdb==1.3.0
RUN . ~/.bashrc && conda activate clip_cn_cpu && pip install onnx onnxruntime onnxmltools fastapi uvicorn python-multipart
RUN . ~/.bashrc && conda activate clip_cn_cpu &&  cd /Chinese-CLIP && pip install -e .
ENV PYTHONPATH=${PYTHONPATH}:/Chinese-CLIP/cn_clip
COPY ./main_clip_cn.py /Chinese-CLIP/main_clip_cn.py
COPY ./deps/startProject.sh /Chinese-CLIP/deps/startProject.sh
EXPOSE 7863
ENTRYPOINT ["bash", "/Chinese-CLIP/deps/startProject.sh"]

1号进程:

#!/bin/bash
set -u
cd /Chinese-CLIP/ && nohup /root/miniconda3/envs/clip_cn_cpu/bin/python main_clip_cn.py >/log.alg  2>&1 &
echo "----------------------------------------"
echo "running..."
while [ 1 ]
doecho "sleep"sleep 3600
done

启动容器服务:

docker run -d -p7863:7863 kevinchina/deeplearning:clip_chinese

访问接口:

import requests
import torch
# 模型可选 ["RN50", "ViT-B-16", "ViT-L-14", "ViT-L-14-336", "ViT-H-14"]
url = "http://home.elvisiky.com:7863/cal_image_feature"
with open("no_text.png", "rb") as f:files = {"fileb": ("no_text.png", f)}  # 将文件字段键设置为 "fileb"data = {"model_name": "ViT-B-16"}  # 传递 model_name 作为表单数据res1 = requests.post(url=url, files=files, data=data, timeout=None)print(res1.json())url = "http://home.elvisiky.com:7863/cal_text_feature"
data = {"text": "夕阳", "model_name": "ViT-B-16"}  # 传递 model_name 作为表单数据
res2 = requests.post(url=url, data=data, timeout=None)
print(res2.json())data = {"text": "怪兽", "model_name": "ViT-B-16"}  # 传递 model_name 作为表单数据
res3 = requests.post(url=url, data=data, timeout=None)
print(res3.json())image_vector = res1.json()["data"]
text_vector = res2.json()["data"] + res3.json()["data"]
vector1 = torch.tensor(image_vector)
vector2 = torch.tensor(text_vector)
inner = vector1 @ vector2.t()
print("一张图片和多个文本的内积", inner)
logits_per_image = 100 * inner
print("换算成softmax概率", logits_per_image.softmax(dim=-1))

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

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

相关文章

制造业进销存管理怎么做?

进销存是什么?生产制造业如何进行进销存管理?制造进销存都能为企业提供什么?进销存管理系统的优势?本文将带大家深入浅出的聊聊制造进销存,全面剖析制造进销存的前世今生。 接下来我会通过一些例子为大家深入浅出的讲…

数据结构与算法—时间复杂度和空间复杂度

目录 1. 什么是数据结构? 2.什么是算法? 3、算法的复杂度 4、时间复杂度 (1) 时间复杂度的概念: (2) 大O的渐进表示法: 六个例题: (3) 时间复杂度对比: 两个例题: OJ题分析时间复杂度…

【快速入门】JVM之类加载机制与Native

感慨: 如何定义一个合格的Java程序员,Java程序员要了解掌握哪些知识点,网上的面试题太多了,后端需要了解掌握的知识点太多太多了,Java基础、数据结构、异常、多线程、Spring、Spring boot、事务、算法、数据库&#xf…

Transformer [Attention is All You Need]

(一)论文部分 Abstract (1)The best performing models also connect the encoder and decoder through an attention mechanism. 最好的性能通过注意力机制将编码器和解码器连接在一起。 (2)the Transformer, based solely on attention mechanisms,…

家政预约服务APP小程序搭建,功能支持定制

家政预约服务APP小程序搭建,功能支持定制,后期升级无忧.团队自主研发家政APP系统,正版源码,售后技术保障! 家政系统客户端 搭建专属私城流量平台,让您插上互联网的翅膀。找月嫂,育婴…

Vue思考题_01v-for与v-if的优先级谁更高

目录 vue2vue3 官方文档上说不推荐将v-for与v-if在同一个标签上使用&#xff0c;因为两者优先级并不明显。 那么到底是那个指令的优先级比较高呢&#xff1f; 在vue2与vue3中答案是相反的。 vue2 在vue2中将2个指令放在同一个标签上 <template><ul><li v-fo…

竞赛 深度学习 大数据 股票预测系统 - python lstm

文章目录 0 前言1 课题意义1.1 股票预测主流方法 2 什么是LSTM2.1 循环神经网络2.1 LSTM诞生 2 如何用LSTM做股票预测2.1 算法构建流程2.2 部分代码 3 实现效果3.1 数据3.2 预测结果项目运行展示开发环境数据获取 最后 0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天…

怎么防止重要文件夹丢失?文件夹安全如何保护?

我们在使用电脑的过程中&#xff0c;会将重要数据放在文件夹中&#xff0c;那么&#xff0c;我们该怎么防止重要文件夹丢失呢&#xff1f;下面我们就一起来了解一下。 EFS加密 EFS加密可以对于NTFS卷上的文件夹进行加密&#xff0c;加密后的文件夹将只允许加密时登录系统的用户…

zookeeper节点数据类型介绍及集群搭建

一、zookeeper介绍 zookeeper官网&#xff1a;Apache ZooKeeper zookeeper是一个分布式协调框架&#xff0c;保证的是CP&#xff0c;即一致性和分区容错性&#xff1b;zookeeper是一个分布式文件存储系统&#xff0c;文件节点可以存储数据&#xff0c;监听子文件节点等可以实…

艺术字画雕刻经营配送商城小程序的作用是什么

一副传神且精致的绘画/雕塑品不仅具有很好的观赏性&#xff0c;更具备售卖属性&#xff0c;当然由于产品本身本身的局限性&#xff0c;无论开店还是线上朋友圈推广&#xff0c;都难有效果。 通过【雨科】平台搭建字画雕刻经营商城&#xff0c;将所有产品线上售卖&#xff0c;电…

“开启中文智能之旅:探秘超乎想象的 Llama2-Chinese 大模型世界”

“开启中文智能之旅&#xff1a;探秘超乎想象的 Llama2-Chinese 大模型世界” 1.国内Llama2最新下载地址 本仓库中的代码示例主要是基于Hugging Face版本参数进行调用&#xff0c;我们提供了脚本将Meta官网发布的模型参数转换为Hugging Face支持的格式&#xff0c;可以直接通过…

QT CmakeLists配置python

这是exe目录&#xff0c;要放到这里&#xff0c;要放到这里&#xff0c;要放到这里。 find_package(PythonLibs 3.6 REQUIRED) include_directories(${PYTHON_INCLUDE_DIRS})set(PY python/libs/) set(PY_LIBS ${PY}_tkinter ${PY}python3 ${PY}python36 ${PY}python36_d) targ…

排序算法——直接插入排序

一、介绍 插入排序就是将前两个元素排好&#xff0c;再将第三个元素通过与前边的元素比较后插入适当的位置&#xff0c;再将第四个元素插入&#xff0c;不断重复插入与前边元素比较的操作&#xff0c;直到将元素都排列好。 演示如下&#xff1a; 视频演示&#xff1a;…

京东数据分析平台:9月中上旬白酒消费市场数据分析

9月份&#xff0c;围绕白酒的热点不断。9月5日&#xff0c;瑞幸咖啡官微发布消息称&#xff0c;瑞幸与贵州茅台跨界合作推出的酱香拿铁刷新单品纪录&#xff0c;首日销量突破542万杯&#xff0c;销售额破1亿元。9月14日&#xff0c;贵州茅台官微发布消息称与德芙推出联名产品“…

软件培训测试高级工程师多测师肖sir__html之作业11

html之作业 案例1&#xff1a; 截图&#xff1a; 代码&#xff1a; <!DOCTYPE html> <html><head><meta charset"UTF-8"><title>表单</title></head><body><table style"background-color:red" bo…

【C++】list基本接口+手撕 list(详解迭代器)

父母就像迭代器&#xff0c;封装了他们的脆弱...... 手撕list目录&#xff1a; 一、list的常用接口及其使用 1.1list 构造函数与增删查改 1.2list 特殊接口 1.3list 排序性能分析 二、list 迭代器实现&#xff08;重点难点&#xff09; 关于迭代器的引入知识&#xff1a…

Mac mov转mp4,详细转换步骤

Mac mov转mp4怎么转&#xff1f;视频文件格式为.mov是由Apple公司所开发的特殊格式。因其只能在苹果设备上播放&#xff0c;与他人分享时就会变得困难。为此&#xff0c;我们通常会选择使用MP4这种最受欢迎的视频格式。在日常使用中&#xff0c;MP4成为了大家首选的视频格式。而…

onlyoffice历史版本功能实现

一&#xff1a;开启客户端配置 如果不开启&#xff0c;回调请求里面的history和changeUrl是空 二&#xff1a;客户端主要实现2个回调函数 1.实现onRequestHistory事件&#xff0c;该事件会在ui点击查看历史的时候发起,用于展示历史列表 关键在于获取到history的内容&#xff…

写爬虫?前端er何必用python

前言 说起网络爬虫,很多人第一时间想到python,但爬虫并非只能用python实现,虽然网上大部分爬虫文章都在说python爬虫,但对于前端程序员来说,我觉得js才是最屌的(对于简单爬取任务来说,复杂的我暂时没碰到~),下面说说我的经验(是的,仅限本人经验),希望能给各位前…

Oracle修改数据之后提交事务如何回滚?

在 MySQL 和 Oracle 数据库中&#xff0c;事务提交后都无法回滚。 在 MySQL 中&#xff0c;恢复机制是通过回滚日志&#xff08;undo log&#xff09;实现的&#xff0c;所有事务进行的修改都会先记录到这个回滚日志中&#xff0c;然后在对数据库中的对应行进行写入。当事务已经…