Bert基础(十八)--Bert实战:NER命名实体识别

2024-04-24 14:44

本文主要是介绍Bert基础(十八)--Bert实战:NER命名实体识别,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

1、命名实体识别介绍

1.1 简介

命名实体识别(NER)是自然语言处理(NLP)中的一项关键技术,它的目标是从文本中识别出具有特定意义或指代性强的实体,并对这些实体进行分类。这些实体通常包括人名、地名、组织机构名、日期、时间、专有名词等。NER在许多实际应用中都非常重要,如信息提取、文本挖掘、机器翻译、自动摘要等。
NER的任务主要分为两部分:

  1. 实体的边界识别:这部分任务是要确定文本中实体的起始和结束位置,即在文本中准确地定位出实体的边界。
  2. 确定实体的类型:在识别出实体的边界之后,还需要确定每个实体的具体类型,如人名、地名、机构名等。

例如,在处理文本“马云在杭州创建了阿里巴巴”时,NER系统需要识别出“阿里巴巴”是一个组织机构名,“马云”是一个人名,“杭州”是一个地名。

NER的技术实现通常涉及机器学习、深度学习等方法,通过训练模型来识别和分类文本中的实体。随着深度学习技术的发展,NER的准确率和效率有了显著提高,成为NLP领域研究和应用的热点之一。我们今天使用transformers库来实现一下。

1.2 标注方法

序列标注的方法中有多种标注方式:BIO、BIOSE、IOB、BILOU、BMEWO,其中前三种最为常见。

  • BIO:标识实体的开始,中间部分和非实体部分

    • b代表“开始”(表示命名实体的开始,即NE)
    • I代表“内部”(表示该词在NE内部)
    • o代表‘outside’(表示这个单词只是一个NE之外的普通单词)
  • BIOSE:增加S单个实体情况的标注和增加E实体的结束标识

    • b代表“开始”(表示一个NE的开始)
    • I代表“内部”(表示该词在NE内部)
    • o代表‘outside’(表示这个单词只是一个NE之外的普通单词)
    • e代表‘end’(表示这个词是一个NE的结尾)
    • s代表“singleton”(表示单个单词是一个NE)
  • IOB (即IOB-1):三位序列标注法(B-begin,I-inside,O-outside),IOB与BIO字母对应的含义相同,其不同点是IOB中,标签B仅用于两个连续的同类型命名实体的边界区分,不用于命名实体的起始位置,这里举个例子:

    • 词序列:(word)(word)(word)(word)(word)(word)
      IOB标注:(I-loc)(I-loc)(B-loc)(I-loc)(o)(o)
      BIO标注:(B-loc)(I-loc)(B-loc)(I-loc)(o)(o)
      在命名实体识别(NER)中,BIO和IOB都使用B、I、O三种标签来标注实体,但是它们在使用B标签的方式上有所不同。
      在BIO标注方法中,B标签用于表示一个实体的开始,I标签用于表示实体的内部,而O标签用于表示非实体词。每个实体都由一个B标签开始,后面跟着零个或多个I标签。
      IOB标注方法与BIO类似,但是在IOB中,B标签有特殊的用途。它仅用于标记两个连续的同类型命名实体的边界,而不是用于标记一个实体的开始。这意味着,如果一个实体紧接着另一个同类型的实体,那么第二个实体不会以B标签开始,而是以I标签开始。
      在这个例子中,前两个词是同一个实体的组成部分,因此在IOB标注中,它们都以I-loc开始。当遇到一个新的同类型实体时,使用B-loc来标记它们的边界。而在BIO标注中,每个实体的开始都使用B标签。
      总的来说,IOB和BIO的主要区别在于如何处理连续的命名实体。IOB在处理这种情况时更加精细,但是在实际应用中,BIO因为其简单性和直观性而被更广泛地使用。

    • 因为IOB的整体效果不好,所以出现了IOB-2,约定了所有命名实体均以B tag开头。这样IOB-2就与BIO的标注方式等价了。

      • I表示实体内部
      • B表示实体开始
      • O表示实体外部
      • 标记说明
        B-Person人名开始
        I- Person人名中间
        B-Organization组织名开始
        I-Organization组织名中间
        O非命名实体

1.3 评价指标

  • 精准率:度量模型的精确度/准确度。 它是正确识别的正值(真正)与所有识别出的正值之间的比率。 精准率指标显示正确标记的预测实体的数量。
    Precision = #True_Positive / (#True_Positive + #False_Positive)

  • 召回率:度量模型预测实际正类的能力。 这是预测的真正值与实际标记的结果之间的比率。 召回率指标显示正确的预测实体的数量。
    Recall = #True_Positive / (#True_Positive + #False_Negatives)

  • F1 分数:F1 分数是精准率和召回率的函数。 在精准率和召回率之间进行平衡时,需要用到它。
    F1 Score = 2 * Precision * Recall / (Precision + Recall)

举例说明

词组Gold标签Predict标签
B-PERB-PER
I-PERI-PER
OO
B-LOCB-LOC
I-LOCI-LOC
OO
OO
OO
B-ORGB-ORG
I-ORGI-ORG
I-ORGO
I-ORGO

例子中一共有三个实体,

  • 预测了三个实体,预测对了2个,所以精准率= 2 3 \frac{2}{3} 32
  • 样本中实际游3个实体,预测对了2个,召回率= 2 3 \frac{2}{3} 32
  • F1 Score = 2 * Precision * Recall / (Precision + Recall) = 2* 2 3 \frac{2}{3} 32* 2 3 \frac{2}{3} 32/( 2 3 \frac{2}{3} 32+ 2 3 \frac{2}{3} 32) = 2 3 \frac{2}{3} 32

2 实战

基本步骤:

1 加载数据集
2 数据预处理
3 创建模型
4 创建评估函数
5 创建训练器
6 训练模型
7 评估
8 预测

2.1 加载数据集

打开hugging face,
在这里插入图片描述
我们这里使用msra_ner数据集

ner_datasets = load_dataset("msra_ner", cache_dir="./data")
ner_datasets
DatasetDict({train: Dataset({features: ['id', 'tokens', 'ner_tags'],num_rows: 45001})test: Dataset({features: ['id', 'tokens', 'ner_tags'],num_rows: 3443})
})

查看数据

print(ner_datasets["train"][0])
{'id': '0', 'tokens': ['当', '希', '望', '工', '程', '救', '助', '的', '百', '万', '儿', '童', '成', '长', '起', '来', ',', '科', '教', '兴', '国', '蔚', '然', '成', '风', '时', ',', '今', '天', '有', '收', '藏', '价', '值', '的', '书', '你', '没', '买', ',', '明', '日', '就', '叫', '你', '悔', '不', '当', '初', '!'], 'ner_tags': [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]}

这个数据集的第一条好像没有被标注,我们来看下,这个数据集的标注类型

ner_datasets["train"].features
{'id': Value(dtype='string', id=None),'tokens': Sequence(feature=Value(dtype='string', id=None), length=-1, id=None),'ner_tags': Sequence(feature=ClassLabel(names=['O', 'B-PER', 'I-PER', 'B-ORG', 'I-ORG', 'B-LOC', 'I-LOC'], id=None), length=-1, id=None)}
label_list = ner_datasets["train"].features["ner_tags"].feature.names
label_list
['O', 'B-PER', 'I-PER', 'B-ORG', 'I-ORG', 'B-LOC', 'I-LOC']

这个是IOB-2类型

2.2 数据预处理

tokenizer = AutoTokenizer.from_pretrained("bert-base-chinese")
tokenizer(ner_datasets["train"][0]["tokens"], is_split_into_words=True) {'input_ids': [101, 2496, 2361, 3307, 2339, 4923, 3131, 1221, 4638, 4636, 674, 1036, 4997, 2768, 7270, 6629, 3341, 8024, 4906, 3136, 1069, 1744, 5917, 4197, 2768, 7599, 3198, 8024, 791, 1921, 3300, 3119, 5966, 817, 966, 4638, 741, 872, 3766, 743, 8024, 3209, 3189, 2218, 1373, 872, 2637, 679, 2496, 1159, 8013, 102], 'token_type_ids': [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 'attention_mask': [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]}  

前面我们查看数据时,看到数据其实已经分词了, 对于已经做好tokenize的数据,要指定is_split_into_words参数为True

# 借助word_ids 实现标签映射
def process_function(examples):tokenized_exmaples = tokenizer(examples["tokens"], max_length=128, truncation=True, is_split_into_words=True)labels = []for i, label in enumerate(examples["ner_tags"]):word_ids = tokenized_exmaples.word_ids(batch_index=i)label_ids = []for word_id in word_ids:if word_id is None:label_ids.append(-100)else:label_ids.append(label[word_id])labels.append(label_ids)tokenized_exmaples["labels"] = labelsreturn tokenized_exmaples

这段代码是一个处理函数,用于对给定的例子进行标记映射。它接受一个包含文本和相应命名实体识别(NER)标签的字典作为输入,并返回一个包含标记化文本和映射标签的字典。以下是该函数的详细解释:

  1. tokenizer:这是一个标记器函数,用于将文本转换为标记。它接受文本、最大长度、截断标志和是否按单词分割的标志作为输入。
  2. tokenized_exmaples = tokenizer(examples["tokens"], max_length=128, truncation=True, is_split_into_words=True):这行代码使用标记器函数对输入文本进行标记化处理,并将结果存储在tokenized_exmaples变量中。最大长度设置为128,如果文本长度超过128,则进行截断。同时,标记器会按单词进行分割。
  3. labels = []:这是一个空列表,用于存储映射后的标签。
  4. for i, label in enumerate(examples["ner_tags"])::这行代码遍历输入字典中的NER标签,并为每个标签分配一个索引i
  5. word_ids = tokenized_exmaples.word_ids(batch_index=i):这行代码获取标记化文本中的单词ID。word_ids是一个列表,其中包含与每个标记对应的单词ID。如果标记是一个子单词,则其单词ID与其前一个标记的单词ID相同;如果标记是一个特殊标记(如CLS或SEP),则其单词ID为None。
  6. label_ids = []:这是一个空列表,用于存储映射后的标签ID。
  7. for word_id in word_ids::这行代码遍历word_ids列表中的每个单词ID。
  8. if word_id is None::这行代码检查单词ID是否为None。如果是,说明当前标记是一个特殊标记,我们将标签ID设置为-100。
  9. else::如果单词ID不是None,说明当前标记是一个单词的一部分。我们将输入标签中的相应标签ID添加到label_ids列表中。
  10. labels.append(label_ids):这行代码将label_ids列表添加到labels列表中。
  11. tokenized_exmaples["labels"] = labels:这行代码将labels列表添加到标记化示例字典中。
  12. return tokenized_exmaples:这行代码返回包含标记化文本和映射标签的字典。

总之,这个处理函数使用标记器对输入文本进行标记化处理,并将输入标签映射到标记化文本上。映射后的标签将用于后续的命名实体识别任务。

tokenized_datasets = ner_datasets.map(process_function, batched=True)
tokenized_datasets
DatasetDict({train: Dataset({features: ['id', 'tokens', 'ner_tags', 'input_ids', 'token_type_ids', 'attention_mask', 'labels'],num_rows: 45001})test: Dataset({features: ['id', 'tokens', 'ner_tags', 'input_ids', 'token_type_ids', 'attention_mask', 'labels'],num_rows: 3443})
})

找一个数据看一下

print(tokenized_datasets["train"][5])
{'id': '5', 'tokens': ['我', '们', '是', '受', '到', '郑', '振', '铎', '先', '生', '、', '阿', '英', '先', '生', '著', '作', '的', '启', '示', ',', '从', '个', '人', '条', '件', '出', '发', ',', '瞄', '准', '现', '代', '出', '版', '史', '研', '究', '的', '空', '白', ',', '重', '点', '集', '藏', '解', '放', '区', '、', '国', '民', '党', '毁', '禁', '出', '版', '物', '。'], 'ner_tags': [0, 0, 0, 0, 0, 1, 2, 2, 0, 0, 0, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 4, 4, 0, 0, 0, 0, 0, 0], 'input_ids': [101, 2769, 812, 3221, 1358, 1168, 6948, 2920, 7195, 1044, 4495, 510, 7350, 5739, 1044, 4495, 5865, 868, 4638, 1423, 4850, 8024, 794, 702, 782, 3340, 816, 1139, 1355, 8024, 4730, 1114, 4385, 807, 1139, 4276, 1380, 4777, 4955, 4638, 4958, 4635, 8024, 7028, 4157, 7415, 5966, 6237, 3123, 1277, 510, 1744, 3696, 1054, 3673, 4881, 1139, 4276, 4289, 511, 102], 'token_type_ids': [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 'attention_mask': [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], 'labels': [-100, 0, 0, 0, 0, 0, 1, 2, 2, 0, 0, 0, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 4, 4, 0, 0, 0, 0, 0, 0, -100]}

2.3 创建模型

# 对于所有的非二分类任务,切记要指定num_labels,否则就会device错误
model = AutoModelForTokenClassification.from_pretrained("bert-base-chinese", num_labels=len(label_list))

2.4 创建评估函数

seqeval = evaluate.load("seqeval")
seqeval

这里使用seqeval进行计算,我们使用开头那个例子来看一下

词组Gold标签Predict标签
B-PERB-PER
I-PERI-PER
OO
B-LOCB-LOC
I-LOCI-LOC
OO
OO
OO
B-ORGB-ORG
I-ORGI-ORG
I-ORGO
I-ORGO
references = [["B-PER", "I-PER", "O", "B-LOC", "I-LOC", "O", "O", "O", "B-ORG", "I-ORG", "I-ORG", "I-ORG"]]
predictions = [["B-PER", "I-PER", "O", "B-LOC", "I-LOC", "O", "O", "O", "B-ORG", "I-ORG", "O", "O"]]results = seqeval.compute(predictions=predictions, references=references){'LOC': {'precision': 1.0, 'recall': 1.0, 'f1': 1.0, 'number': 1},'ORG': {'precision': 0.0, 'recall': 0.0, 'f1': 0.0, 'number': 1},'PER': {'precision': 1.0, 'recall': 1.0, 'f1': 1.0, 'number': 1},'overall_precision': 0.6666666666666666,'overall_recall': 0.6666666666666666,'overall_f1': 0.6666666666666666,'overall_accuracy': 0.8333333333333334}

创建评估函数

import numpy as npdef eval_metric(pred):predictions, labels = predpredictions = np.argmax(predictions, axis=-1)# 将id转换为原始的字符串类型的标签true_predictions = [[label_list[p] for p, l in zip(prediction, label) if l != -100]for prediction, label in zip(predictions, labels) ]true_labels = [[label_list[l] for p, l in zip(prediction, label) if l != -100]for prediction, label in zip(predictions, labels) ]result = seqeval.compute(predictions=true_predictions, references=true_labels, mode="strict", scheme="IOB2")return {"f1": result["overall_f1"]}

这段代码定义了一个评估指标函数eval_metric,用于评估序列标注模型在命名实体识别(NER)任务上的性能。函数的输入是一个元组pred,其中包含模型预测的分数和真实的标签。代码使用numpy库来处理数组运算,并使用seqeval库来计算评估指标。
以下是代码的详细解释:

  1. import numpy as np:导入numpy库,用于执行高效的数学运算。
  2. def eval_metric(pred)::定义一个名为eval_metric的函数,它接受一个参数pred,这是一个包含模型预测分数和真实标签的元组。
  3. predictions, labels = pred:将pred元组解包为两个变量predictionslabels,分别存储模型预测的分数和真实的标签。
  4. predictions = np.argmax(predictions, axis=-1):使用numpyargmax函数沿最后一个轴(即类别轴)找到每个样本的最大预测分数的索引,这些索引对应于预测的标签ID。
  5. true_predictions = [...]:这是一个列表推导式,用于将预测的标签ID转换为原始的字符串类型的标签。对于每个预测和标签序列,它遍历它们并创建一个新的列表,其中只包含标签不是-100的元素。label_list[p]用于将标签ID转换为字符串标签。
  6. true_labels = [...]:这是另一个列表推导式,与true_predictions类似,但它用于转换真实的标签ID为字符串标签。
  7. result = seqeval.compute(predictions=true_predictions, references=true_labels, mode="strict", scheme="IOB2"):这行代码使用seqeval库的compute函数来计算评估指标。true_predictions是模型的预测,true_labels是真实的标签。mode参数设置为"strict",表示严格评估模式,scheme参数设置为"IOB2",表示使用IOB2标签格式。
  8. return {"f1": result["overall_f1"]}:函数返回一个字典,其中包含整体的F1分数,这是NER任务中常用的评估指标。
    总之,这个函数用于评估模型在NER任务上的性能,它将模型输出的分数转换为标签,并使用seqeval库来计算F1分数。

2.5 创建训练器

args = TrainingArguments(output_dir="models_for_ner",per_device_train_batch_size=64,per_device_eval_batch_size=128,evaluation_strategy="epoch",save_strategy="epoch",metric_for_best_model="f1",load_best_model_at_end=True,logging_steps=50,num_train_epochs=1,report_to=['tensorboard']
)

为了节省时间我们只训练一遍num_train_epochs=1

2.6 模型训练

trainer = Trainer(model=model,args=args,train_dataset=tokenized_datasets["train"],eval_dataset=tokenized_datasets["test"],compute_metrics=eval_metric,data_collator=DataCollatorForTokenClassification(tokenizer=tokenizer)
)
trainer.train()

在这里插入图片描述
F1达到了0.946

2.7 评估

trainer.evaluate(eval_dataset=tokenized_datasets["test"]){'eval_loss': 0.02092777192592621,'eval_f1': 0.9463030643800956,'eval_runtime': 16.7905,'eval_samples_per_second': 205.056,'eval_steps_per_second': 1.608,'epoch': 1.0}

2.8 预测

# 如果模型是基于GPU训练的,那么推理时要指定device
# 对于NER任务,可以指定aggregation_strategy为simple,得到具体的实体的结果,而不是token的结果
ner_pipe = pipeline("token-classification", model=model, tokenizer=tokenizer, device=0, aggregation_strategy="simple")res = ner_pipe("马云在杭州创建了阿里巴巴")
res[{'entity_group': 'PER','score': 0.9968899,'word': '马 云','start': 0,'end': 2},{'entity_group': 'LOC','score': 0.99767697,'word': '杭 州','start': 3,'end': 5},{'entity_group': 'ORG','score': 0.98138344,'word': '阿 里 巴 巴','start': 8,'end': 12}]

完整代码

这篇关于Bert基础(十八)--Bert实战:NER命名实体识别的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/932049

相关文章

使用 sql-research-assistant进行 SQL 数据库研究的实战指南(代码实现演示)

《使用sql-research-assistant进行SQL数据库研究的实战指南(代码实现演示)》本文介绍了sql-research-assistant工具,该工具基于LangChain框架,集... 目录技术背景介绍核心原理解析代码实现演示安装和配置项目集成LangSmith 配置(可选)启动服务应用场景

如何通过海康威视设备网络SDK进行Java二次开发摄像头车牌识别详解

《如何通过海康威视设备网络SDK进行Java二次开发摄像头车牌识别详解》:本文主要介绍如何通过海康威视设备网络SDK进行Java二次开发摄像头车牌识别的相关资料,描述了如何使用海康威视设备网络SD... 目录前言开发流程问题和解决方案dll库加载不到的问题老旧版本sdk不兼容的问题关键实现流程总结前言作为

0基础租个硬件玩deepseek,蓝耘元生代智算云|本地部署DeepSeek R1模型的操作流程

《0基础租个硬件玩deepseek,蓝耘元生代智算云|本地部署DeepSeekR1模型的操作流程》DeepSeekR1模型凭借其强大的自然语言处理能力,在未来具有广阔的应用前景,有望在多个领域发... 目录0基础租个硬件玩deepseek,蓝耘元生代智算云|本地部署DeepSeek R1模型,3步搞定一个应

在Java中使用ModelMapper简化Shapefile属性转JavaBean实战过程

《在Java中使用ModelMapper简化Shapefile属性转JavaBean实战过程》本文介绍了在Java中使用ModelMapper库简化Shapefile属性转JavaBean的过程,对比... 目录前言一、原始的处理办法1、使用Set方法来转换2、使用构造方法转换二、基于ModelMapper

Java实战之自助进行多张图片合成拼接

《Java实战之自助进行多张图片合成拼接》在当今数字化时代,图像处理技术在各个领域都发挥着至关重要的作用,本文为大家详细介绍了如何使用Java实现多张图片合成拼接,需要的可以了解下... 目录前言一、图片合成需求描述二、图片合成设计与实现1、编程语言2、基础数据准备3、图片合成流程4、图片合成实现三、总结前

nginx-rtmp-module构建流媒体直播服务器实战指南

《nginx-rtmp-module构建流媒体直播服务器实战指南》本文主要介绍了nginx-rtmp-module构建流媒体直播服务器实战指南,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有... 目录1. RTMP协议介绍与应用RTMP协议的原理RTMP协议的应用RTMP与现代流媒体技术的关系2

C语言小项目实战之通讯录功能

《C语言小项目实战之通讯录功能》:本文主要介绍如何设计和实现一个简单的通讯录管理系统,包括联系人信息的存储、增加、删除、查找、修改和排序等功能,文中通过代码介绍的非常详细,需要的朋友可以参考下... 目录功能介绍:添加联系人模块显示联系人模块删除联系人模块查找联系人模块修改联系人模块排序联系人模块源代码如下

Golang操作DuckDB实战案例分享

《Golang操作DuckDB实战案例分享》DuckDB是一个嵌入式SQL数据库引擎,它与众所周知的SQLite非常相似,但它是为olap风格的工作负载设计的,DuckDB支持各种数据类型和SQL特性... 目录DuckDB的主要优点环境准备初始化表和数据查询单行或多行错误处理和事务完整代码最后总结Duck

Python中的随机森林算法与实战

《Python中的随机森林算法与实战》本文详细介绍了随机森林算法,包括其原理、实现步骤、分类和回归案例,并讨论了其优点和缺点,通过面向对象编程实现了一个简单的随机森林模型,并应用于鸢尾花分类和波士顿房... 目录1、随机森林算法概述2、随机森林的原理3、实现步骤4、分类案例:使用随机森林预测鸢尾花品种4.1

MySQL中my.ini文件的基础配置和优化配置方式

《MySQL中my.ini文件的基础配置和优化配置方式》文章讨论了数据库异步同步的优化思路,包括三个主要方面:幂等性、时序和延迟,作者还分享了MySQL配置文件的优化经验,并鼓励读者提供支持... 目录mysql my.ini文件的配置和优化配置优化思路MySQL配置文件优化总结MySQL my.ini文件