使用Bert+BiLSTM+CRF训练 NER任务

使用的数据集在这里E-Commercial NER Dataset / 电商NER数据集_数据集-阿里云天池

针对面向电商的命名实体识别研究,我们通过爬取搜集了淘宝商品文本的标题,并标注了4大类,9小类的实体类别。具体类型及实体数量如下

针对面向电商的命名实体识别研究,我们通过爬取搜集了淘宝商品文本的标题,并标注了4大类,9小类的实体类别。具体类型及实体数量如下:
 

enter image description here

每个文件数据格式相同,都为根据BI schema标注的NER数据

模型使用BERT + BiLSTM + CRF 该结构通过结合强大的预训练语言表示、序列建模能力和标签依赖建模,能够有效地提升命名实体识别任务的准确性和鲁棒性。这种组合在实际应用中已经显示出优越的性能,尤其是在复杂的语言理解任务中。

BERT + BiLSTM + CRF 是一种常用于命名实体识别(NER)任务的深度学习模型结构。下面是对这个模型结构的详细解释,以及它在 NER 任务中的好处。

模型结构

BERT(Bidirectional Encoder Representations from Transformers)
  • 预训练模型:BERT 是一种基于 Transformer 的预训练语言模型,能够捕捉到上下文信息。它通过双向编码器考虑上下文中的所有单词,从而生成每个单词的上下文嵌入。
  • 词向量表示:在 NER 任务中,BERT 可以生成高质量的词嵌入,帮助模型更好地理解文本中的语义。
BiLSTM(Bidirectional Long Short-Term Memory)
  • 序列建模:BiLSTM 是一种改进的 LSTM(长短期记忆网络),能够同时考虑序列的前向和后向信息。通过双向处理,BiLSTM 可以更好地捕捉上下文依赖关系。
  • 特征提取:在 NER 任务中,BiLSTM 能够从 BERT 输出的嵌入中提取序列特征,并对它们进行时间序列建模。
CRF(Conditional Random Field)
  • 序列标注:CRF 是一种用于序列标注的概率模型,可以考虑标签之间的依赖关系。它在输出层对 BiLSTM 的结果进行后处理,以确保输出标签满足特定的序列约束。
  • 提高准确性:CRF 能够通过学习标签之间的关系(例如,实体标签之间的相互影响)来增强模型的预测能力。

应用在 NER 任务中的好处

1. 上下文理解
  • BERT 的强大能力:BERT 提供丰富的上下文信息,能够更好地理解文本中的语义,从而提高对实体的识别能力。
2. 序列建模
  • BiLSTM 的双向特性:通过双向处理,BiLSTM 能够捕捉到前后文的依赖关系,改进对实体边界的识别。
3. 标签依赖建模
  • CRF 的序列约束:CRF 考虑了输出标签的依赖关系,能够避免产生不合理的标签序列(例如,"B-PER" 后面直接跟 "B-LOC")。
4. 提高性能
  • 综合优势:结合 BERT、BiLSTM 和 CRF 的优点,可以显著提高 NER 任务的性能,尤其是在处理复杂的实体关系和多样的语境时。

 模型model.py内容

# -*- coding: utf-8 -*-
import torch
import torch.nn as nn
from transformers import BertModel
from TorchCRF import CRFclass Bert_BiLSTM_CRF(nn.Module):def __init__(self, tag_to_ix, embedding_dim=768, hidden_dim=256):super(Bert_BiLSTM_CRF, self).__init__()self.tag_to_ix = tag_to_ixself.tagset_size = len(tag_to_ix)self.hidden_dim = hidden_dimself.embedding_dim = embedding_dimself.bert = BertModel.from_pretrained('bert-base-chinese')self.lstm = nn.LSTM(input_size=embedding_dim,hidden_size=hidden_dim // 2,num_layers=2,bidirectional=True,batch_first=True)self.dropout = nn.Dropout(p=0.1)self.linear = nn.Linear(hidden_dim, self.tagset_size)self.crf = CRF(self.tagset_size)def _get_features(self, sentence):with torch.no_grad():outputs = self.bert(sentence)embeds = outputs.last_hidden_stateenc, _ = self.lstm(embeds)enc = self.dropout(enc)feats = self.linear(enc)return featsdef forward(self, sentence, tags, mask, is_test=False):emissions = self._get_features(sentence)if not is_test:  # 训练阶段,返回lossloss = -self.crf(emissions, tags, mask)return loss.mean()else:return self.crf.viterbi_decode(emissions, mask)

统计下数据集中出现的多少种类型的标注

# 统计标签类型的Python脚本def count_labels(file_path):labels = set()  # 使用集合来存储唯一标签with open(file_path, 'r', encoding='utf-8') as file:for line in file:parts = line.strip().split()  # 按空格分割每一行if len(parts) > 1:  # 确保这一行有标签label = parts[-1]  # 标签通常在最后一列labels.add(label)  # 将标签添加到集合中return labels# 指定文件路径
file_path = 'dataset/train.txt'
unique_labels = count_labels(file_path)# 输出结果
print(f"总共有 {len(unique_labels)} 种类型的标签:")
for label in unique_labels:print(label)

该数据集里共出现了 9 种类型的标签:
 

I-HCCX
I-HPPX
B-HCCX
B-XH
I-XH
I-MISC
O
B-HPPX
B-MISC

标注遵循了一种常见的命名实体识别标注方案:

  • B- 表示实体的开始。
  • I- 表示实体的内部部分。
  • O 表示非实体部分。
  • 实体类别(如 HCCX、HPPX、XH、MISC)根据具体任务和数据集定义。例如HCCX为产品类型,HPPX是品牌名称,XH 是产品型号

下面开始编写训练需要工具

# -*- coding: utf-8 -*-
'''
@Author: Chenrui
@Date: 2021-9-30
@Description: This file is for implementing Dataset. 
@All Right Reserve
'''import torch
from torch.utils.data import Dataset
from transformers import BertTokenizerbert_model = 'bert-base-chinese'
tokenizer = BertTokenizer.from_pretrained(bert_model)
'''
<PAD>: 用于填充序列,确保输入长度一致。
[CLS]: 表示序列的开始,常用于分类任务。
[SEP]: 用于分隔不同的输入序列,帮助模型理解输入的结构。
'''
labels = ['B-HCCX','I-HPPX','I-HCCX','I-MISC','B-XH','O','I-XH','B-MISC','B-HPPX']VOCAB = ('<PAD>', '[CLS]', '[SEP]')# 将 VOCAB 转换为列表以便修改
vocab_list = list(VOCAB)# 添加 labels 到 vocab_list
vocab_list.extend(labels)# 将列表转换回元组
VOCAB = tuple(vocab_list)tag2idx = {tag: idx for idx, tag in enumerate(VOCAB)}
idx2tag = {idx: tag for idx, tag in enumerate(VOCAB)}
MAX_LEN = 256 - 2class NerDataset(Dataset):''' Generate our dataset '''def __init__(self, f_path):self.sents = []self.tags_li = []with open(f_path, 'r', encoding='utf-8') as f:lines = [line.split('\n')[0] for line in f.readlines() if len(line.strip()) != 0]tags = [line.split(' ')[1] for line in lines]words = [line.split(' ')[0] for line in lines]word, tag = [], []for char, t in zip(words, tags):if char != '。':word.append(char)tag.append(t)else:if len(word) > MAX_LEN:self.sents.append(['[CLS]'] + word[:MAX_LEN] + ['[SEP]'])self.tags_li.append(['[CLS]'] + tag[:MAX_LEN] + ['[SEP]'])else:self.sents.append(['[CLS]'] + word + ['[SEP]'])self.tags_li.append(['[CLS]'] + tag + ['[SEP]'])word, tag = [], []def __getitem__(self, idx):words, tags = self.sents[idx], self.tags_li[idx]token_ids = tokenizer.convert_tokens_to_ids(words)label_ids = [tag2idx[tag] for tag in tags]seqlen = len(label_ids)return token_ids, label_ids, seqlendef __len__(self):return len(self.sents)def PadBatch(batch):maxlen = 512token_tensors = torch.LongTensor([i[0] + [0] * (maxlen - len(i[0])) for i in batch])label_tensors = torch.LongTensor([i[1] + [0] * (maxlen - len(i[1])) for i in batch])mask = (token_tensors > 0)return token_tensors, label_tensors, mask

训练的主程序

# -*- coding: utf-8 -*-
'''
@Author: Chenrui
@Date: 2023-9-30
@Description: This file is for training, validating and testing.
@All Right Reserve
'''import argparse
import warningsimport numpy as np
import torch
from sklearn import metrics
from torch.utils import data
from transformers import AdamW, get_linear_schedule_with_warmupfrom models import Bert_BiLSTM_CRF
from utils import NerDataset, PadBatch, tag2idx, idx2tag, labelswarnings.filterwarnings("ignore", category=DeprecationWarning)
# os.environ['CUDA_VISIBLE_DEVICES'] = '0'if __name__ == "__main__":best_model = None_best_val_loss = 1e18_best_val_acc = 1e-18parser = argparse.ArgumentParser()parser.add_argument("--batch_size", type=int, default=64)parser.add_argument("--lr", type=float, default=0.001)parser.add_argument("--n_epochs", type=int, default=40)parser.add_argument("--trainset", type=str, default="./dataset/train.txt")parser.add_argument("--validset", type=str, default="./dataset/dev.txt")parser.add_argument("--testset", type=str, default="./dataset/test.txt")ner = parser.parse_args()device = 'cuda' if torch.cuda.is_available() else 'cpu'model = Bert_BiLSTM_CRF(tag2idx).to(device)print('Initial model Done.')train_dataset = NerDataset(ner.trainset)eval_dataset = NerDataset(ner.validset)test_dataset = NerDataset(ner.testset)print('Load Data Done.')'''torch.utils.data.DataLoader 是 PyTorch 中用于加载数据的一个强大工具。它的主要作用是将数据集分成小批量(batches),并提供迭代器以便在训练模型时方便地访问数据。以下是 DataLoader 的一些主要功能和使用方法。'''train_iter = data.DataLoader(dataset=train_dataset,batch_size=ner.batch_size,shuffle=True,num_workers=4,collate_fn=PadBatch)eval_iter = data.DataLoader(dataset=eval_dataset,batch_size=(ner.batch_size) // 2,shuffle=False,num_workers=4,collate_fn=PadBatch)test_iter = data.DataLoader(dataset=test_dataset,batch_size=(ner.batch_size) // 2,shuffle=False,num_workers=4,collate_fn=PadBatch)# optimizer = optim.Adam(self.model.parameters(), lr=ner.lr, weight_decay=0.01)optimizer = AdamW(model.parameters(), lr=ner.lr, eps=1e-6)# Warmuplen_dataset = len(train_dataset)epoch = ner.n_epochsbatch_size = ner.batch_sizetotal_steps = (len_dataset // batch_size) * epoch if len_dataset % batch_size == 0 \else (len_dataset // batch_size + 1) * epochwarm_up_ratio = 0.1  # Define 10% steps'''get_linear_schedule_with_warmup 是 Hugging Face 的 transformers 库中提供的一个函数,用于创建一个学习率调度器。它的主要作用是在训练过程中动态调整学习率,具体通过以下几个步骤实现:主要功能学习率预热(Warmup):在训练的初始阶段,学习率从 0 逐渐增加到设定的最大学习率。这有助于防止模型在训练初期出现较大的更新,从而导致不稳定。线性衰减(Linear Decay):在预热阶段结束后,学习率会线性地递减到 0。这种衰减方式通常能有效地提高模型的收敛性。'''scheduler = get_linear_schedule_with_warmup(optimizer=optimizer,num_warmup_steps=warm_up_ratio * total_steps,num_training_steps=total_steps)print('Start Train...,')for epoch in range(1, ner.n_epochs + 1):train(epoch, model, train_iter, optimizer, scheduler, device)candidate_model, loss, acc = validate(epoch, model, eval_iter, device)if loss < _best_val_loss and acc > _best_val_acc:best_model = candidate_model_best_val_loss = loss_best_val_acc = accprint("=============================================")y_test, y_pred = test(best_model, test_iter, device)print(metrics.classification_report(y_test, y_pred, labels=labels, digits=3))# 保存模型torch.save(model.state_dict(), 'ner_task_model.pth')

导入部分工具的用途

  • argparse:用于解析命令行参数。
  • warnings:用于控制警告信息。
  • numpytorch:用于数值计算和深度学习。
  • sklearn.metrics:用于评估模型性能。
  • transformers:提供 AdamW 优化器和学习率调度器

train训练函数,功能训练模型并输出损失。

def train(e, model, iterator, optimizer, scheduler, device):model.train()losses = 0.0step = 0for i, batch in enumerate(iterator):step += 1x, y, z = batchx = x.to(device)y = y.to(device)z = z.to(device)loss = model(x, y, z)losses += loss.item()loss.backward()optimizer.step()scheduler.step()optimizer.zero_grad()print("Epoch: {}, Loss:{:.4f}".format(e, losses / step))
  • e: 当前的训练轮数。
  • model: NER 模型。
  • iterator: 数据加载器。
  • optimizer: 优化器。
  • scheduler: 学习率调度器。
  • device: 设备(CPU 或 GPU)

validate函数,验证集上评估模型性能,计算损失和准确率,并返回模型、损失和准确率。

def validate(e, model, iterator, device):# 设置模型为评估模式model.eval()Y, Y_hat = [], []losses = 0step = 0with torch.no_grad():for i, batch in enumerate(iterator):step += 1x, y, z = batchx = x.to(device)y = y.to(device)z = z.to(device)y_hat = model(x, y, z, is_test=True)loss = model(x, y, z)losses += loss.item()for j in y_hat:Y_hat.extend(j)mask = (z == 1)y_orig = torch.masked_select(y, mask)Y.append(y_orig.cpu())Y = torch.cat(Y, dim=0).numpy()Y_hat = np.array(Y_hat)acc = (Y_hat == Y).mean() * 100print("Epoch: {}, Val Loss:{:.4f}, Val Acc:{:.3f}%".format(e, losses / step, acc))return model, losses / step, acc

test函数,在测试集上评估模型性能,返回真实标签和预测标签

def test(model, iterator, device):model.eval()Y, Y_hat = [], []with torch.no_grad():for i, batch in enumerate(iterator):x, y, z = batchx = x.to(device)z = z.to(device)y_hat = model(x, y, z, is_test=True)# Save predictionfor j in y_hat:Y_hat.extend(j)# Save labelsmask = (z == 1).cpu()y_orig = torch.masked_select(y, mask)Y.append(y_orig)Y = torch.cat(Y, dim=0).numpy()y_true = [idx2tag[i] for i in Y]y_pred = [idx2tag[i] for i in Y_hat]return y_true, y_pred

上面代码实现了一个完整的 NER 训练、验证和测试流程,使用了 BERT、BiLSTM 和 CRF 模型结构。通过 argparse 解析输入参数,使用 PyTorch 处理数据和模型训练,使用 DataLoader 创建迭代器以便于批量处理数据,置 AdamW 优化器和学习率调度器,进行多个训练轮次,每个轮次调用训练和验证函数,最终评估模型性能并保存最佳模型。这是一个典型的深度学习项目框架,适用于序列标注任务。

运行训练

python main.py --n_epochs 1000

查看到准确率稍低, 先不管了把模型文件下载下来本地先试试识别提取能力

下载保存的模型文件

测试该模型代码

import osimport torch
from torch.utils.data import Datasetfrom models import Bert_BiLSTM_CRF
from utils import tokenizer, tag2idx, MAX_LEN, idx2tagdevice = 'cuda' if torch.cuda.is_available() else 'cpu'class SequenceLabelingDataset(Dataset):def __init__(self, lines):self.lines = linesself.sents, word = [], []words = [line[0] for line in lines]for char in words:if char != '。':word.append(char)else:if len(word) > MAX_LEN:self.sents.append(['[CLS]'] + word[:MAX_LEN] + ['[SEP]'])else:self.sents.append(['[CLS]'] + word + ['[SEP]'])word = []def __len__(self):return len(self.sents)def item(self):words = self.sents[0]token_ids = tokenizer.convert_tokens_to_ids(words)seqlen = len(token_ids)return token_ids, None, seqlendef get_token_ids(self, batch):maxlen = 512token_tensors = torch.LongTensor([i[0] + [0] * (maxlen - len(i[0])) for i in batch])label_tensors = Nonemask = (token_tensors > 0)return token_tensors, label_tensors, mask# 示例数据
data_string = '2005复刻正版戏曲光盘1dvd视频碟片河南曲剧优秀剧目集锦。'print('data_string', data_string)
data = [(char,) for char in data_string]
print('data', data)# 创建数据集和 DataLoader
dataset = SequenceLabelingDataset(data)current_directory = os.getcwd()
model_path = os.path.join(current_directory, 'model', 'ner_task_model.pth')model = Bert_BiLSTM_CRF(tag2idx).to(device)
model.load_state_dict(torch.load(model_path, map_location=device))
model.eval()def predict(model, batch, device):x, y, z = batch[0]maxlen = 512token_tensors = torch.LongTensor([i[0] + [0] * (maxlen - len(i[0])) for i in batch])mask = (token_tensors > 0)predict = model(token_tensors, None, mask, is_test=True)  # 使用生成的 maskreturn predicttry:batch = [dataset.item()]predict = predict(model, batch, device)print('predict', predict)label_ids = [idx2tag[tag] for tag in predict[0]]print('label_ids', label_ids)
except Exception as e:print('predict throw exception', e)# 提取实体
entities = {'HCCX': [],'HPPX': [],'MISC': [],'XH': [],'O': []
}# 当前实体存储
current_entities = {'HCCX': [],'HPPX': [],'MISC': [],'XH': [],'O': []
}# 按照标签提取实体
for i, label in enumerate(label_ids):word = data_string[i] if i < len(data_string) else ''if 'B-' in label:entity_type = label[2:]  # 获取实体类型current_entities[entity_type].append(word)elif 'I-' in label:entity_type = label[2:]  # 获取实体类型if current_entities[entity_type]:  # 确保当前实体存在current_entities[entity_type].append(word)else:# 处理当前实体for entity_type in current_entities:if current_entities[entity_type]:entities[entity_type].append("".join(current_entities[entity_type]))current_entities[entity_type] = []# 处理最后一个实体
for entity_type in current_entities:if current_entities[entity_type]:entities[entity_type].append("".join(current_entities[entity_type]))# 输出结果
for entity_type, entity_list in entities.items():print(f"提取的{entity_type}实体:", entity_list)

返回结果

实体识别出来一些

现在NER任务基本都是大模型去做,相比大语言模型小模型越来越势微越来越不受重视,前公司在知识图谱上搞得globalpointer 任务做NER得产品已经彻底放弃,以至于我们还有些用该产品得客户已经无法得到支持了,但作为NER 入门得例子还是值得拿出来写写

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

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

相关文章

iptables防火墙总结

iptables防火墙总结 四表五链 P 默认策略&#xff0c;-p 协议 出口防火墙&#xff0c;默认配置&#xff0c;使用Forword&#xff0c;采用-P 例题&#xff1a;设置iptables防火墙默认不允许任何数据包进入&#xff0c;相应命令是&#xff1a; Iptables -t filter -P FORWORD -j…

【数据结构】五分钟自测主干知识(十一)

上回&#xff08;半年前&#xff09;我们讲了二叉树&#xff0c;如果我们要找到二叉树某序遍历下的前驱和后继&#xff0c;我们需要对其作动态遍历求得&#xff0c;比较费时&#xff1b;或者给每个结点增加两个指针域prior和next&#xff0c;但比较费空间。 有没有既能省时间&…

【K8S系列】Kubernetes Service 基础知识 详细介绍

在 Kubernetes 中&#xff0c;Service 是一种抽象的资源&#xff0c;用于定义一组 Pod 的访问策略。它为这些 Pod 提供了一个稳定的访问入口&#xff0c;解决了 Pod 可能频繁变化的问题。本文将详细介绍 Kubernetes Service 的类型、功能、使用场景、DNS 和负载均衡等方面。 1.…

Openlayers高级交互(2/20):清除所有图层的有效方法

Openlayers项目中,经常会放置很多的图层,在业务操作的时候,会做出删除所有图层的行为。这里面给出了一个详细的方法,能够有效的解决 清除所有图层的问题。 效果图 专栏名称内容介绍Openlayers基础实战 (72篇)专栏提供73篇文章,为小白群体提供基础知识及示例演示,能解决…

[软件工程]—桥接(Brige)模式与伪码推导

桥接&#xff08;Brige&#xff09;模式与伪码推导 1.基本概念 1.1 动机 由于某些类型的固有的实现逻辑&#xff0c;使它们具有两个变化的维度&#xff0c;乃至多个维度的变化。如何应对这种“多维度的变化”&#xff1f;如何利用面向对象技术是的类型可以轻松的沿着两个乃至…

022_matrix_dancing_in_Matlab中求解一个超简单的矩阵问题

矩阵体操 首先&#xff0c;可以复习一下向量、矩阵和索引的基础知识。 向量约定矩阵约定矩阵索引 一般而言&#xff0c;我们利用进行计算大概就是以下的步骤&#xff1a; #mermaid-svg-UovF0Uldf5XxntJi {font-family:"trebuchet ms",verdana,arial,sans-serif;fo…

MFC实现以不规则PNG图片作为窗口背景

效果图 显示的不规则PNG图片 头文件 #pragma once #include <gdiplus.h> #pragma comment (lib,"Gdiplus.lib")// CShowBack 对话框class CShowBack : public CDialogEx {DECLARE_DYNAMIC(CShowBack) public:CShowBack(CWnd* pParent nullptr); // 标准构…

C++学习路线(二十二)

构造函数 构造函数作用 在创建一个新的对象时&#xff0c;自动调用的函数&#xff0c;用来进行“初始化”工作:对这个对象内部的数据成员进行初始化。 构造函数特点 1.自动调用(在创建新对象时&#xff0c;自动调用) 2.构造函数的函数名&#xff0c;和类名相同 3.构造函数…

react18中的jsx 底层渲染机制相关原理

jsx 底层渲染机制 渲染 jsx 时&#xff0c;会先解析 jsx&#xff0c;生成一个虚拟 dom(virtual dom)。然后将虚拟 dom 渲染成真实 dom。如果 jsx 中包含事件&#xff0c;会将事件绑定到真实 dom 上。 虚拟 dom 对象&#xff0c;是框架内部构建的一套对象体系&#xff0c;对象…

无废话、光速上手 React-Router

React-Router React Router 是一个用于 React 应用的声明式路由库。它允许开发者通过组件化的方式定义应用的路由结构&#xff0c;使得路由管理更加直观和可维护 安装 pnpm i react-router-dom定义路由 定义路由有两种方式&#xff0c;分别是对象路由和路由组件&#xff0c…

AIGC时代 | 从零到一,打造你的专属AI Chat应用!

文章目录 目标功能概要&#xff08;1&#xff09;Chat 交互界面&#xff08;2&#xff09;流式接口&#xff08;3&#xff09;多轮会话&#xff08;4&#xff09;打字效果 系统架构&#xff08;1&#xff09;大模型服务层&#xff08;2&#xff09;应用服务层&#xff08;3&…

深入解析东芝TB62261FTG,步进电机驱动方案

TB62261FTG是一款由东芝推出的两相双极步进电机驱动器&#xff0c;采用了BiCD工艺&#xff0c;能够提供高效的电机控制。这款芯片具有多种优秀的功能&#xff0c;包括PWM斩波、内置电流调节、低导通电阻的MOSFET以及多种步进操作模式&#xff0c;使其非常适合用于需要精确运动控…

微信小程序的日期区间选择组件的封装和使用

组件化开发是一种将大型软件系统分解为更小、更易于管理和复用的独立模块或组件的方法。这种方法在现代软件开发中越来越受到重视&#xff0c;尤其是在前端开发领域。微信小程序的日期区间选择组件的使用 wxml 代码 <view><view bind:tap"chooseData">…

第一个Qt程序

创建项目 进入ui界面拖一个按钮 在头文件中添加函数说明 #ifndef HELLO_H #define HELLO_H#include <QMainWindow>QT_BEGIN_NAMESPACE namespace Ui { class Hello; } QT_END_NAMESPACEclass Hello : public QMainWindow {Q_OBJECTpublic:Hello(QWidget *parent nullpt…

上海亚商投顾:沪指缩量调整 NMN概念股逆势大涨

上海亚商投顾前言&#xff1a;无惧大盘涨跌&#xff0c;解密龙虎榜资金&#xff0c;跟踪一线游资和机构资金动向&#xff0c;识别短期热点和强势个股。 一.市场情绪 市场全天震荡调整&#xff0c;三大指数午后一度跌超1%&#xff0c;北证50指数则涨超4%&#xff0c;北交所个股…

前端发送请求格式

1.multipart/form-data格式发送请求参数 什么时候用&#xff1a; 当后端API要求以表单的形式接收数据时&#xff0c;比如<input type"text" name"username">和<input type"password" name"password">&#xff0c;这些数据…

html全局属性、框架标签

常用的全局属性&#xff1a; 属性名含义id 给标签指定唯一标识&#xff0c;注意&#xff1a;id是不能重复的。 作用&#xff1a;可以让label标签与表单控件相关联&#xff1b;也可以与css、JavaScript配合使用。 注意&#xff1a;不能再以下HTML元素中使用&#xff1a;<hea…

自动化部署-01-jenkins安装

文章目录 前言一、下载安装二、启动三、问题3.1 jdk版本问题3.2 端口冲突3.3 系统字体配置问题 四、再次启动五、配置jenkins5.1 解锁5.2 安装插件5.3 创建管理员用户5.4 实例配置5.5 开始使用5.6 完成 总结 前言 spingcloud微服务等每次部署到服务器上&#xff0c;都需要本地…

鸿蒙到底是不是纯血?到底能不能走向世界?

关注卢松松&#xff0c;会经常给你分享一些我的经验和观点。 2016年5月鸿蒙系统开始立项。 2018年美国开始经济战争&#xff0c;其中一项就是制裁华为&#xff0c;不让华为用安卓。 2019年8月9日华为正式发布鸿蒙系统。问题就出在这里&#xff0c;大家可以仔细看。 安卓一…

从零开始学五笔(三):横区字根

从 1 区开始讲解字根&#xff1a; 先介绍按键的区位号、口诀内容、口诀说明然后列每个字根能组成什么汉字&#xff0c;难拆字将用中括号标出 ‍ G 键 区位号&#xff1a;11 口诀&#xff1a;王旁青头戋&#xff08;兼&#xff09;五一 说明&#xff1a; 王旁&#xff1a…