论文速读 - Cleaner Pretraining Corpus Curation with Neural Web Scraping

news2024/11/26 11:37:15

这是论文 Cleaner Pretraining Corpus Curation with Neural Web Scraping 的速读笔记,同时简要分析这篇论文作者的实现代码. 论文的主要工作是提出了基于神经网络的高效crawler.

这里先澄清scrapercrawler的区别,一图胜千言.

web-crawler-vs-web-scraper

Abstract

The web contains large-scale, diverse, and abundant information to satisfy the informationseeking needs of humans. Through meticulous data collection, preprocessing, and curation, webpages can be used as a fundamental data resource for language model pretraining. However, when confronted with the progressively revolutionized and intricate nature of webpages, rule-based/feature-based web scrapers are becoming increasingly inadequate. This paper presents a simple, fast, and effective Neural web Scraper (NeuScraper) to help extract primary and clean text contents from webpages. Experimental results show that NeuScraper surpasses the baseline scrapers by achieving more than a 20% improvement, demonstrating its potential in extracting higher-quality data to facilitate the language model pretraining. All of the code is available at https: //github.com/OpenMatch/NeuScraper.

网络包含大规模、多样化和丰富的信息,以满足人类的信息需求。通过精心的数据收集、预处理和整理,网页可以作为语言模型预训练的基本数据资源。然而,面对不断革新和复杂的网页性质,基于规则/基于特征的scraper越来越显得不够用。本文介绍了一种简单、快速且有效的神经网络网页scraper(NeuScraper),以帮助从网页中提取主要和干净的文本内容。实验结果表明,NeuScraper通过实现超过20%的改进,超过了基线scraper,展示了其在提取更高质量数据以促进语言模型预训练方面的潜力。所有代码可在 https://github.com/OpenMatch/NeuScraper 上找到。

摘要中指出了网页因为多样化,以及丰富的信息,可以作为LLM训练的基本数据源,而现有的scraper不够用。因此作者提出了NeuScraper这种基于神经网络的方法,帮助从网页中提取主要干净文本, 并且在和baseline比较的时候,得到20%的性能提升.

Introduction

简介中,介绍了当前已有的一些工作。

LLMs在各种NLP任务重,随着模型参数规模的扩大,展现出了非常令人印象深刻的性能. 但在对scaling law研究的过程中发现表面,模型参数大小和训练数据的大小应该等比例的扩大,这在过去足够大的训练数据集方面构成了挑战,甚至引发了对数据稀缺的担忧.

为了给预训练整理更多的数据,研究人员更加注重从网络上收集更有价值的数据。像CommonCrawl这样的网络爬取数据集已经被广泛用于预训练,促进了语言模型的发展。然而,先前的研究表明,即使经过积极的清洗,CommonCrawl提供的预提取文本的质量仍然未能达到预期,原因在于广告、横幅、超链接等有害内容通常混杂在页面的主要内容中,因此仅提取这些主要内容会为预训练带来大量噪声. 先前的工作主要是rule basedheuristic,但在网页越来越复杂的现在,不仅效果不好,且很难维护这些scraper.

因此论文提出了一个简单、快速且有效的神经网络scraper(NeuScraper),它被设计用来从网页中提取主要内容。NeuScraper采用了浅层神经网络架构,并整合了布局信息以实现高效的抓取。实验表明,NeuScraper超越了基线,性能提升了20%,并为语言模型预训练生成了更高质量的语料库,尤其在GPU上展现了非常好的处理速度.
在这里插入图片描述
上图展现了NeuScraper的Content Extraction处理流程,可以看到,将原始的html提取转化为序列,经过NeuScarper的处理(事实上为分类处理),提取出需要的内容.

Related Work

相关工作中主要介绍了从网页中提取内容的一些方法, 例如基于规则和基于特征.

基于规则

基于规则的网页抓取方法,以及其中涉及的一些关键技术和挑战:

  1. 网页包装器(Web Wrappers):
  • 早期的网页抓取常使用网页包装器作为起点。
  • 网页包装器通常需要人工设计或使用包装器归纳系统生成。
  • 每个网页都需要定制包装器,难以处理大规模网页。
  1. 文档对象模型(DOM)树:
  • 更常见的方法是创建DOM树,用于构建基于规则的抓取器或比较网页。
  • DOM树表示网页的结构化数据,便于提取内容。
  1. 其他辅助技术:
  • 标签累积分布(Tag Cumulative Distributions)
  • 文本密度(Text Density)
  • 标签比例(Tag Ratios)
  • 这些技术可以帮助从网页中提取有用内容。

总的来说,基于规则的网页抓取方法需要针对不同网页定制规则和工具,难以扩展到大规模应用。研究者们提出了各种辅助技术,如DOM树、标签分布等,来改进内容提取的效果。但是,这些方法仍然需要大量的人工参与和专业知识,自动化程度有限。

基于特征

介绍了基于特征的网页内容提取方法,与基于规则的方法相比,这种方法更加灵活和智能。

  1. 基于特征的方法概述:

    • 将网页分割成多个块
    • 从这些块中提取大量手工设计的特征
    • 将这些特征输入到机器学习模型中进行分类
  2. 网页分块方法:

    • 基于HTML标签或DOM树结构制定规则
    • 将网页划分为若干个块
  3. 特征提取:
    从每个块中提取多种特征,包括:

    • 标记特征
    • 文本/文档特征
    • 语言学特征
    • 结构特征
    • 视觉特征
    • 基于DOM树的特征
  4. 机器学习模型:
    将提取的特征输入到各种机器学习模型中,如:

    • 支持向量机(SVM)
    • 条件随机场(CRF)
    • 逻辑回归
    • 卷积神经网络(CNN)
  5. 分类目标:
    判断每个块中的文本是否属于网页的主要内容

这种基于特征的方法相比于简单的规则基础方法更加灵活,能够处理更复杂的网页结构。通过结合机器学习技术,这种方法可以自动学习判断主要内容的规则,而不需要人工设计复杂的规则集。这种方法在处理多样化的网页时具有更好的适应性和准确性。

Neural Web Scraper

这部分介绍NeuScraper模型的工作流程和backbone.

先前的工作已经证明了结构和视觉特征在帮助识别主要内容方面的有效性。因此,为了保留网页布局信息,我们依赖DOM树结构将网页转换为文本序列。具体来说,我们使用BeautifulSoup4工具包为每个网页构建DOM树,对树进行深度优先遍历,并将访问顺序视为表示节点的附加位置信息。在这个过程中,只有包含纯文本的节点、表格节点(用<table>标记)和列表节点(用<ol>、<ul>或<dl>标记)被保留下来,以产生最终的文本序列 X = { x 1 , x 2 , ⋯   , x n } X = \{x_1, x_2, \cdots, x_n\} X={x1,x2,,xn},其中n表示保留的DOM节点的数量。处理后,网络爬取任务主要涉及确定节点xi是否包含网页的主要内容以供评估。

然后是这篇论文的最核心工作,如何处理这个序列 X = { x 1 , x 2 , ⋯   , x n } X = \{x_1, x_2, \cdots, x_n\} X={x1,x2,,xn}.

为了保障高效处理,NeuScraper用了XML-Roberta对这个序列进行encode, 把DOM node编码成 dim=768的向量.
h i = XLMRoberta-Layer ⁡ 1 ( x i ) , \begin{align} h_i=\operatorname{XLMRoberta-Layer}^1(x_i), \end{align} hi=XLMRoberta-Layer1(xi),
这里的 h i h_i hi实际上就是BERT中的[CLS]标签.

经过这一步处理后,结果会被放到一个3层transformer架构中(8个Attention head)
h i ^ = Transformer ⁡ ( Linear ⁡ ( h i ) ) , \begin{align} \hat{h_i} = \operatorname{Transformer}(\operatorname{Linear}(h_i)), \end{align} hi^=Transformer(Linear(hi)),
根据前人的工作,这些DOM node可以被分为6类:

  1. primary content
  2. heading
  3. title,
  4. paragraph
  5. table
  6. list

根据这6个分类,用 y k y^k yk表示第 k k k种标签(也就是类型), 计算DOM node属于某一种类型的概率 P ( y i k = 1 ∣ x i ) P(y_i^k=1\mid x_i) P(yik=1xi)
P ( y i k = 1 ∣ x i ) = Sigmoid ⁡ ( MLP ⁡ ( h i ^ ) ) \begin{align} P(y_i^k=1\mid x_i) = \operatorname{Sigmoid}(\operatorname{MLP}(\hat{h_i})) \end{align} P(yik=1xi)=Sigmoid(MLP(hi^))
然后与标签 Y i k \mathcal{Y}_i^k Yik计算交叉熵损失得到损失函数:
L = ∑ k = 1 6 ∑ i = 1 n CrossEntropy ⁡ ( P ( y i k ∣ x i ) , Y i k ) \begin{align} L = \sum_{k=1}^{6}\sum_{i=1}^n\operatorname{CrossEntropy}(P(y_i^k\mid x_i),\mathcal{Y}_i^k) \end{align} L=k=16i=1nCrossEntropy(P(yikxi),Yik)
至此,主要的工作就完成了.

实现

论文作者在github上给出了模型的实现, 本文简要分析一下模型结构代码src/scraper/model.py

import math
import torch.nn as nn
from metrics import *
from transformers import BertConfig, XLMRobertaConfig, XLMRobertaModel
from transformers.models.bert.modeling_bert import BertEncoder


class PositionalEncoding(nn.Module):
    def __init__(self, d_model, dropout=0.1, max_len=5000):
        super(PositionalEncoding, self).__init__()
        self.dropout = nn.Dropout(p=dropout)
        self.LayerNorm = nn.LayerNorm(d_model)

        pe = torch.zeros(max_len, d_model)
        position = torch.arange(0, max_len / 2, dtype=torch.float).unsqueeze(1)
        position = position.repeat(1, 2).view(-1, 1)
        div_term = torch.exp(torch.arange(0, d_model, 2).float() * (-math.log(10000.0) / d_model))
        pe[:, 0::2] = torch.sin(position * div_term)
        pe[:, 1::2] = torch.cos(position * div_term)
        pe = pe.unsqueeze(0).transpose(0, 1)
        self.register_buffer("pe", pe)

    def forward(self, x):
        x = x + self.pe[: x.size(0), :]
        return self.dropout(x)

class MLP(nn.Module):
    def __init__(self, input_dim, output_dim, hidden_dim):
        super(MLP, self).__init__()
        self.input_dim = input_dim
        self.output_dim = output_dim
        self.hidden_dim = hidden_dim
        current_dim = input_dim
        self.layers = nn.ModuleList()
        if len(hidden_dim) >= 1:
            for hdim in hidden_dim:
                self.layers.append(nn.Linear(current_dim, hdim))
                self.layers.append(nn.ReLU())
                current_dim = hdim
        self.layers.append(nn.Linear(current_dim, output_dim))

    def forward(self, x):
        for layer in self.layers[:-1]:
            x = layer(x)
        out = self.layers[-1](x)
        return out


class ContentExtractionTextEncoder(nn.Module):
    def __init__(self, config):
        super().__init__()

        self.model_version = config.model_version
        self.sigmoid = nn.Sigmoid()
        self.relu = nn.ReLU()
        self.max_sequence_len = config.max_sequence_len
        self.num_classes = config.num_classes
        self.text_in_emb_dim = config.text_in_emb_dim
        self.text_emb_dim = config.text_emb_dim
        self.hidden = MLP(self.text_emb_dim, config.num_classes, []) 
        self.max_token_len = config.max_token_len
        self.enable_positional_encoding = not config.disable_positional_encoding

        print("Positional Encoding Enabled?: " + str(self.enable_positional_encoding))

        if self.enable_positional_encoding:
            self.pos_encoder = PositionalEncoding(d_model=self.text_emb_dim, max_len=config.max_sequence_len)

        self.textlinear = nn.Linear(
            config.text_in_emb_dim, config.text_emb_dim
        )  # 768 -> 256

        configuration = BertConfig(
            num_hidden_layers=config.num_layers,
            num_attention_heads=config.num_heads,
            hidden_size=self.text_emb_dim,
            intermediate_size=1024,
            output_hidden_states=False,
            output_attentions=False,
        )
        self.encoder = BertEncoder(configuration)

        text_roberta_config = XLMRobertaConfig.from_pretrained(
            "xlm-roberta-base",
            num_attention_heads=12,
            num_hidden_layers=config.text_encoder_num_hidden_layer,
        )
        self.text_roberta = XLMRobertaModel(text_roberta_config)


    def forward(self, x):
        [token_ids, token_masks] = x
        seq_len = self.max_sequence_len
        text_in_emb_dim = self.text_in_emb_dim
        max_token_len = self.max_token_len

        token_ids = token_ids.view(-1, max_token_len)  # [batch * max_sequence_len, max_token_len]
        token_masks = token_masks.view(-1, max_token_len)  # [batch * max_sequence_len, max_token_len]

        features = []

        text_output = self.text_roberta(input_ids=token_ids, attention_mask=token_masks)
        all_text_emb = text_output.pooler_output.reshape(-1, seq_len, text_in_emb_dim)

        text_x = self.textlinear(all_text_emb)
        features.append(text_x)

        text_visual_x = torch.cat(features, 2)

        if self.enable_positional_encoding:
            text_visual_x = text_visual_x.permute(1, 0, 2)

            text_visual_x = self.pos_encoder(text_visual_x)
            text_visual_x = text_visual_x.permute(1, 0, 2)

             
        if 'bert' in self.model_version:
            emb_output = self.encoder(text_visual_x, head_mask=[None, None, None])[0]
        else:
            emb_output = text_visual_x

        x_hidden = self.hidden(emb_output)
        output = self.sigmoid(x_hidden)
        return output

代码很清晰,主要定义了:

  • PostionEncoding 实现位置编码,这个在主网络中属于可选项
  • MLP 用来做最后的classification
  • ContentExtractionTextEncoder 处理已经经过XLMRobertaModelBertEncoder处理的序列

整个实现借助了XLMRoberta 这个强大的模型.

总结

这篇论文的内容不是非常多,也很容易理解,提出了对html内容提取的很好的思路,也很容易实现。但根据介绍训练的话需要8卡A100, 个人还是没法进行from scratch 的训练的,finetune方法正在研究中,欢迎沟通交流.

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

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

相关文章

openpnp - bug - 散料飞达至少定义2个物料

文章目录 openpnp - bug - 散料飞达至少定义2个物料笔记END openpnp - bug - 散料飞达至少定义2个物料 笔记 散料飞达上定义的物料个数用完了&#xff0c;现在只需要一个料就可以。 用顶部相机去找编带上是否还有一个单独的料&#xff0c;找到了。 定义散料飞达的料为1个&…

springboot使用attachment方式下载文件损坏问题解决

文章目录 场景解决方式全部代码 场景 之前使用springboot下载文件一直正常&#xff0c;今天新对接一个接口出现文件破损&#xff0c;无法下载。 之前的代码: Overridepublic ResponseEntity<ByteArrayResource> resultExcel(ExcelResultDTO excelResultDTO) {log.info(…

CentOS7系统内核升级

1. 安装新内核 采用离线方式升级 去到下面网站中下载rpm安装包 https://mirrors.coreix.net/elrepo-archive-archive/kernel/el7/x86_64/RPMS/下载 wget https://mirrors.coreix.net/elrepo-archive-archive/kernel/el7/x86_64/RPMS/kernel-lt-5.4.278-1.el7.elrepo.x86_64…

探寻闲鱼libsgmain加解密算法(4) ——JNI入口跳转

关注我的人都知道我一直在学习阿里的加密和算法&#xff0c;除了研究逆向问题&#xff0c;还会把学来的阿里技术用在自己的应用上。 为什么&#xff1f;因为学习大厂的应用&#xff0c;是进步最快的方法。而大厂在安全和加密方面的技术&#xff0c;个人觉得阿里做的是最好的。 …

Maven项目管理工具-初始+环境配置

1. Maven的概念 1.1. 什么是Maven Maven是跨平台的项目管理工具。主要服务于基于Java平台的项目构建&#xff0c;依赖管理和项目信息管理。 理想的项目构建&#xff1a;高度自动化&#xff0c;跨平台&#xff0c;可重用的组件&#xff0c;标准化的流程 maven能够自动下载依…

Maven 不同环境灵活构建

需求: 使用 Maven根据不同的构建环境&#xff08;如开发、测试、生产&#xff09;来定义不同的配置&#xff0c;实现灵活的构建管理。 需要Demo项目的可以参考&#xff1a;我的demo项目 一、项目分层 一般的初创项目不会有特别多的配置文件&#xff0c;所以使用 spring.profile…

Android调用系统相机录像并设置参数

最近要做一个 Android上的录像功能&#xff0c;由于之前做拍照功能完全是用自定义方式&#xff0c;太麻烦。故这次决定直接调用系统相机来录像。 一、添加权限 首先&#xff0c;添加必要的权限 <!-- 授予该程序使用摄像头的权限 --><uses-permission android:name&q…

K8s中TSL证书如何续期

TSL是什么 K8s中的作用是什么&#xff1f; 在 Kubernetes&#xff08;K8s&#xff09;中&#xff0c;TSL 指的是 Transport Layer Security&#xff0c;也就是传输层安全协议。它是用来保护在网络上传输的数据的安全性和隐私性。 TSL 在 Kubernetes 中的作用包括&#xff1a;…

B+树(B树的改进)

目录 一、什么是B树&#xff1f; 二、B树的性质 1.B树被广泛作为数据库索引的索引结构 2.m个分支的结点有m个元素 3.每个元素对应子结点最大值 4.多级索引结构 5.叶子结点层包含所有元素 三、B树和B树的区别 四、B树的查找 1.顺序查找 2.随机查找 3.范围查找 一、什…

JVM机制

文章目录 JVM 简介JVM内存划分堆&#xff08;线程共享&#xff09;Java虚拟机栈&#xff08;线程私有&#xff09;本地方法栈&#xff08;线程私有&#xff09;程序计数器&#xff08;线程私有&#xff09;方法区&#xff08;线程共享&#xff09; JVM类加载机制类加载过程双亲…

校园表白墙源码修复版

此校园表白墙源码基于thinkphp&#xff0c;因为时代久远有不少bug&#xff0c;经本人修复已去除大部分bug&#xff0c;添加了美化元素。 https://pan.quark.cn/s/1f9b3564c84b https://pan.baidu.com/s/1bb9vu9VV2jJoo9-GF6W3xw?pwd7293 https://caiyun.139.com/m/i?2hoTc…

群控系统服务端开发模式-应用开发-业务架构逻辑开发准备工作

安装与仓库已经调整完毕&#xff0c;现在开发业务架构逻辑&#xff0c;其次再开发功能逻辑。业务架构逻辑开发与功能逻辑开发不是一回事&#xff0c;一定要明白。业务架构指的是做某一件事或是某一种类型的事的逻辑&#xff0c;在互联网web应用中通常指一套系统的外在逻辑&…

js 的宏任务和微任务

宏任务 (macro-task) 与微任务 (micro-task) 在 JavaScript 中&#xff0c;宏任务&#xff08;macro-task&#xff09;和微任务&#xff08;micro-task&#xff09;是任务队列&#xff08;task queue&#xff09;中两个不同的任务类型&#xff0c;它们是 JavaScript 异步编程机…

DBeaver查看已保存连接的密码

打开Dbeaver窗口菜单-首选项-工作空间&#xff0c;找到工作空间路径 在文件管理器中打开工作空间路径\General.dbeaver&#xff0c;找到credentials-config.json。 在Linux下&#xff0c;使用如下命令对credentials-config.json文件进行解密 openssl aes-128-cbc -d -K babb4…

13 实战:使用Python和Pygame实现视频运动估计播放器

首先看运行效果: 在多媒体处理领域,视频的运动估计是一个重要的课题。在本文中,我们将详细介绍如何使用Python结合Pygame、OpenCV等库,实现一个支持运动估计的视频播放器。本项目旨在展示如何在Python中处理视频帧,实现块匹配算法进行运动估计,并将结果以可视化的方式呈现…

kafka 分布式(不是单机)的情况下,如何保证消息的顺序消费?

大家好&#xff0c;我是锋哥。今天分享关于【kafka 分布式&#xff08;不是单机&#xff09;的情况下&#xff0c;如何保证消息的顺序消费?】面试题&#xff1f;希望对大家有帮助&#xff1b; kafka 分布式&#xff08;不是单机&#xff09;的情况下&#xff0c;如何保证消息的…

TypeScript基础简介

TypeScript是Javascript的一个超集。 TypeScript在原有的基础之上又添加了编译器类型检查的功能&#xff0c;意味着如果使用ts进行开发&#xff0c;会对变量的类型进行较为严格的验证&#xff0c;防止程序员写出可能出错的代码&#xff0c;规范变成习惯&#xff0c;适合大项目开…

2024年MathorCup妈杯大数据竞赛选题人数发布

经过24个小时&#xff0c;各个平台的相关选题投票、相关文章阅读量等各项数据进行统计&#xff0c;利用之前的评估办法&#xff08;详见注释&#xff09;。在开赛后24小时&#xff0c;我们基本确定各个赛题选题人数&#xff0c;以帮助大家更好地分析赛题局势。 题目人数A46B72 …

【Vulnhub靶场】DC-4

DC-4靶场下载地址https://www.five86.com/downloads/DC-4.zip 本机IP&#xff1a;192.168.118.128 靶机IP&#xff1a;192.168.118.0/24 信息收集 扫描主机存活&#xff0c;扫描端口&#xff0c;扫描服务 第一步扫描出主机ip为192.168.118.141 nmap -sP 192.168.118.0/24 nm…

Java后端面试题:Java基础篇

目录 Java基础 1.请你说说Java中基本数据类型的bit长度&#xff1f; 2.switch支持哪些数据类型&#xff1f;支持long么&#xff1f; 3.讲一下常见编码方式&#xff1f; 4.char能不能存储中文&#xff1f; 5.为什么数组索引从0开始呢&#xff1f;假如从1开始不行吗&#xf…