1654563012
ASCII table - A simple tool to format tables with various row/column options for indentation, indentation character, alignment, padding (left, right, both), padding characters (left, right, both), and in-line whitespace characters.
For Maven declare a dependency in the <dependencies>
section of your POM file.
Dependency declaration in pom.xml
<dependency>
<groupId>de.vandermeer</groupId>
<artifactId>asciitable</artifactId>
<version>0.3.2</version>
</dependency>
compile 'de.vandermeer:asciitable:0.3.2'
For other build systems see Maven Central
Text table with some flexibility for rules and content, alignment, format, padding, margins, and frames:
The main concepts are: table, table context, and table renderer.
The figure below shows all spacing characteristics of a table. The outer rectangle (using +
, -
, and |
characters) marks the most outer part of a table. This is followed by top, bottom, left, and right frame margins.
The next rectangle (using the UTF-8 double line characters) shows an example grid. Inside the grid 4 rows with different column spans are shown.
+--------------------------------------------------------------------------------------------+
| |
| Top Frame Margin |
| |
| ╔════════════════════════╦════════════════════════╦════════════════════════╗ |
| ║ row 1 col 1 / cell 1,1 ║ row 1 col 2 / cell 1,2 ║ row 1 col 3 / cell 1,3 ║ |
| F M ╠════════════════════════╩════════════════════════╬════════════════════════╣ F M |
| r a ║ row 2 col 1&2 / cell 1,1/2 ║ row 2 col 3 / cell 1,3 ║ r a |
| a r ╠════════════════════════╦════════════════════════╩════════════════════════╣ a r |
| m g ║ row 3 col 1 / cell 1,1 ║ row 2 col 2&3 / cell 1,2/3 ║ m g |
| e i ╠════════════════════════╩═════════════════════════════════════════════════╣ e i |
| n ║ row 4 col 1&2&3 / cell 1,1/2/3 ║ n |
| ╚══════════════════════════════════════════════════════════════════════════╝ |
| |
| Bottom Frame Margin |
| |
+--------------------------------------------------------------------------------------------+
The next figure shows the anatomy of a single table cell. The core is the text in the middle. Top and bottom padding can be added (whole lines before and after the text). Left and right padding can be added to the text.
╔══════════════════════════════╗
║ Top Padding ║
║------------------------------║
║ Left | cell | Right ║
║ Padding | text | Padding ║
║------------------------------║
║ Bottom Padding ║
╚══════════════════════════════╝
A paragraph is a collection of text strings. The strings are processed as follows:
line feed and carriage return.
Paragraphs can be formatted using a number of special formats. Currently implemented are
Text in the paragraph can be aligned in multiple different ways:
All lines will use padding to create a paragraph with equal length of each line. The padding on the left and the right depends on the text alignment:
The characters being used for padding can be set separately, so that each site of a line gets a different padding character.
With all excessive white spaces removed, each line only contains single blanks. The exception to this rule are all justified paragraphs (here extra white spaces are added to give the impression of a justified paragraph).
The implementation allows to change the character used for in-text white spaces from the default (a blank) to any other character.
Each line of a paragraph can be started and terminated by a specific (different or identical) string. These strings are outside the text area, i.e. no special formatting is done on those strings.
A paragraph has several margins for the left and right sides as well as for top and bottom. Each margin can be set - the width for let/right side margins and the height for top and bottom margins. Additionally, a character can be set for left/right margins (the same or different characters for each side).
A paragraph can also be framed. A frame is
The frame is set as a frame theme. A number of those themes are provided in the skb-interfaces
package. New themes can be created very easily, using ASCII and/or UTF-8 characters.
While the paragraph only maintains the text, the paragraph context maintains all configurable characteristics of the paragraph (see above). The current implementation directly has
The following characteristics are handled by special objects (one for each), which the context provides access to:
Additionally, the context provides a number of helper methods for rendering
The paragraph can be initialized with a given context or plain, in which case it will create its own context object. Any future characteristics will be added to the paragraph context
The actual rendering of a paragraph is realized by special render objects (i.e. it’s not done in the paragraph or its context). A paragraph can be rendered in two different ways:
No changes are made to the paragraph text or any context settings by any render operation. All required text being processed and calculations being made will happen inside the renderer.
The render methods on the paragraph allow to render it (a) to the width set in the context or (b) to an overall required width. The first option is the most simple one: fill paragraph with text, set width on context, render. The second option can be used by other applications, for instance a table, to get a paragraph of required width.
For any other render operations use the provided standard renderer or create your own render object. The default renderer does currently provide render methods to different width with calculations provided by the context.
Note: coming soon: It also provides render methods that use their own context (i.e. ignore the context set in the paragraph). This allows for extremely flexibility in using the paragraph in many different scenarios.
The standard usage is:
First, create a table.
AsciiTable at = new AsciiTable();
Next, add content (rows and cells). Any text can be added, the renderer will process the text (for instance remove excessive white spaces).
at.addRule();
at.addRow("row 1 col 1", "row 1 col 2");
at.addRule();
at.addRow("row 2 col 1", "row 2 col 2");
at.addRule();
Next, render the table. This will provide the text output using the default settings from the table’s context.
String rend = at.render()
Finally, print the table to standard out.
System.out.println(rend);
The output will be:
┌───────────────────────────────────────┬──────────────────────────────────────┐
│row 1 col 1 │row 1 col 2 │
├───────────────────────────────────────┼──────────────────────────────────────┤
│row 2 col 1 │row 2 col 2 │
└───────────────────────────────────────┴──────────────────────────────────────┘
The following examples are using the classic "Lorem Ipsum" text as content.
Width of 50, 40, and 30 on the same table.
┌────────────────────────┬───────────────────────┐
│row 1 col 1 │row 1 col 2 │
├────────────────────────┼───────────────────────┤
│row 2 col 1 │row 2 col 2 │
└────────────────────────┴───────────────────────┘
┌───────────────────┬──────────────────┐
│row 1 col 1 │row 1 col 2 │
├───────────────────┼──────────────────┤
│row 2 col 1 │row 2 col 2 │
└───────────────────┴──────────────────┘
┌──────────────┬─────────────┐
│row 1 col 1 │row 1 col 2 │
├──────────────┼─────────────┤
│row 2 col 1 │row 2 col 2 │
└──────────────┴─────────────┘
The number of columns a table supports is determined by the first content row added. Here are tables with columns ranging from 1 to 5
┌────────────────────────────┐
│Table Heading │
├────────────────────────────┤
│first row (col1) │
├────────────────────────────┤
│second row (col1) │
└────────────────────────────┘
┌───────────────────────────────┬───────────────────────────────┐
│first row (col1) │some information (col2) │
├───────────────────────────────┼───────────────────────────────┤
│second row (col1) │some information (col2) │
└───────────────────────────────┴───────────────────────────────┘
┌──────────────────────────┬─────────────────────────┬─────────────────────────┐
│first row (col1) │some information (col2) │more info (col3) │
├──────────────────────────┼─────────────────────────┼─────────────────────────┤
│second row (col1) │some information (col2) │more info (col3) │
└──────────────────────────┴─────────────────────────┴─────────────────────────┘
┌───────────────────┬───────────────────┬───────────────────┬──────────────────┐
│first row (col1) │text (col2) │more text (col3) │even more (col4) │
├───────────────────┼───────────────────┼───────────────────┼──────────────────┤
│second row (col1) │text (col2) │more text (col3) │even more (col4) │
└───────────────────┴───────────────────┴───────────────────┴──────────────────┘
┌───────────────┬───────────────┬───────────────┬───────────────┬──────────────┐
│row1 (col1) │text (col2) │text (col3) │text (col4) │text (col5) │
├───────────────┼───────────────┼───────────────┼───────────────┼──────────────┤
│row2 (col1) │text (col2) │text (col3) │text (col4) │text (col5) │
└───────────────┴───────────────┴───────────────┴───────────────┴──────────────┘
The table supports column spans
┌─────────────────────────────────────────────────────────────────────┐
│span all 5 columns │
├───────────────────────────────────────────────────────┬─────────────┤
│span 4 columns │just 1 column│
├─────────────────────────────────────────┬─────────────┴─────────────┤
│span 3 columns │span 2 columns │
├───────────────────────────┬─────────────┴───────────────────────────┤
│span 2 columns │span 3 columns │
├─────────────┬─────────────┴─────────────────────────────────────────┤
│just 1 column│span 4 columns │
├─────────────┼─────────────┬─────────────┬─────────────┬─────────────┤
│just 1 column│just 1 column│just 1 column│just 1 column│just 1 column│
└─────────────┴─────────────┴─────────────┴─────────────┴─────────────┘
Text in cells can be aligned in different ways: justified left, justified, justified right, left, center right. The text alignment can be set on the whole table, a row, or individual cells.
┌─────────────────────────┬─────────────────────────┬─────────────────────────┐
│Lorem ipsum dolor sit│Lorem ipsum dolor sit│Lorem ipsum dolor sit│
│amet, consetetur│amet, consetetur│amet, consetetur│
│sadipscing elitr, sed│sadipscing elitr, sed│sadipscing elitr, sed│
│diam nonumy eirmod tempor│diam nonumy eirmod tempor│diam nonumy eirmod tempor│
│invidunt ut labore et│invidunt ut labore et│invidunt ut labore et│
│dolore magna │dolore magna│ dolore magna│
├─────────────────────────┼─────────────────────────┼─────────────────────────┤
│Lorem ipsum dolor sit │ Lorem ipsum dolor sit │ Lorem ipsum dolor sit│
│amet, consetetur │ amet, consetetur │ amet, consetetur│
│sadipscing elitr, sed │ sadipscing elitr, sed │ sadipscing elitr, sed│
│diam nonumy eirmod tempor│diam nonumy eirmod tempor│diam nonumy eirmod tempor│
│invidunt ut labore et │ invidunt ut labore et │ invidunt ut labore et│
│dolore magna │ dolore magna │ dolore magna│
└─────────────────────────┴─────────────────────────┴─────────────────────────┘
Padding can be added to text in cells above (top) and below (bottom) the text or in front (left) or behind (right) each line. The character for the padding can be set separately.
┌───────────────┬───────────────┐
│vvvvvvvvvvvvvvv│vvvvvvvvvvvvvvv│
│> row 1 col 1 <│> row 1 col 2 <│
│^^^^^^^^^^^^^^^│^^^^^^^^^^^^^^^│
├───────────────┼───────────────┤
│vvvvvvvvvvvvvvv│vvvvvvvvvvvvvvv│
│> row 2 col 1 <│> row 2 col 2 <│
│^^^^^^^^^^^^^^^│^^^^^^^^^^^^^^^│
└───────────────┴───────────────┘
Margins cen be set outside the grid (top, bottom, left, right). Margins and the character used for rendering the margin can be set separately.
vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
>>>┌───────────┬───────────┐<<<<
>>>│row 1 col 1│row 1 col 2│<<<<
>>>├───────────┼───────────┤<<<<
>>>│row 2 col 1│row 2 col 2│<<<<
>>>└───────────┴───────────┘<<<<
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Grids are used to draw a frame around cells. The implementation used TA_Grid
objects from the ascii-utf-themes
package.
┌─────┬─────┐ +-----+-----+ ═════════════ ╒═════╤═════╕ ╔═════╦═════╗
│rc 11│rc 12│ |rc 11|rc 12| rc 11 rc 12 │rc 11│rc 12│ ║rc 11║rc 12║
├─────┼─────┤ +-----+-----+ ═════════════ ╞═════╪═════╡ ╠═════╬═════╣
│rc 21│rc 22│ |rc 21|rc 22| rc 21 rc 22 │rc 21│rc 22│ ║rc 21║rc 22║
└─────┴─────┘ +-----+-----+ ═════════════ ╘═════╧═════╛ ╚═════╩═════╝
Grids can support different rule styles, thus supporting normal, light, strong, and heavy table rules.
═════════════
rc 11 rc 12
─────────────
rc 21 rc 22
▓▓▓▓▓▓▓▓▓▓▓▓▓
rc 31 rc 32
▀▀▀▀▀▀▀▀▀▀▀▀▀
Grids support different themes. A grid theme defines which grid characters from which position should be rendered. All other character will be rendered using a default character, usually blank.
┌─────┬─────┐ ┌ ┐ ┌ ┬ ┐ ───────────
│rc 11│rc 12│ rc 11 rc 12 rc 11 rc 12 rc 11 rc 12 rc 11 rc 12
├─────┼─────┤ ├ ┼ ┤ ───────────
│rc 21│rc 22│ rc 21 rc 22 rc 21 rc 22 rc 21 rc 22 rc 21 rc 22
└─────┴─────┘ └ ┘ └ ┴ ┘ ───────────
┌───────────┐ ───────────
│rc 11│rc 12│ rc 11│rc 12 │rc 11 rc 12│ │rc 11 rc 12 rc 11 rc 12
│ │ │ ─────┼───── │ │ │
│rc 21│rc 22│ rc 21│rc 22 │rc 21 rc 22│ │rc 21 rc 22 rc 21 rc 22
└───────────┘ ───────────
New grids can be easily defined and applied to a table.
#############
&rc 11&rc 12&
+#####+#####+
&rc 21&rc 22&
%#####%#####%
Uniform Resource Identifiers (URIs) can be used in a table. No special rules are applied to them for line breaks. The renderer tries to put as many characters of a URI into a single line as possible.
┌───────────────────────────────────┬───────────────────────────────────┐
│scheme:[//[user:password@]host[:por│scheme:[//[user:password@]host[:por│
│t]][/]path[?query][#fragment] │t]][/]path[?query][#fragment] │
├───────────────────────────────────┴───────────────────────────────────┤
│scheme:[//[user:password@]host[:port]][/]path[?query][#fragment] │
├───────────────────────────────────┬───────────────────────────────────┤
│abc://username:password@example.com│abc://username:password@example.com│
│:123/path/data?key=value#fragid1 │:123/path/data?key=value#fragid1 │
├───────────────────────────────────┴───────────────────────────────────┤
│abc://username:password@example.com:123/path/data?key=value#fragid1 │
├───────────────────────────────────┬───────────────────────────────────┤
│urn:example:mammal:monotreme:echidn│urn:example:mammal:monotreme:echidn│
│a │a │
├───────────────────────────────────┴───────────────────────────────────┤
│urn:example:mammal:monotreme:echidna │
├───────────────────────────────────┬───────────────────────────────────┤
│http://www.example.com/test1/test2 │http://www.example.com/test1/test2 │
├───────────────────────────────────┴───────────────────────────────────┤
│http://www.example.com/test1/test2 │
├───────────────────────────────────┬───────────────────────────────────┤
│mailto:user1@example.com │mailto:firstname.lastname@example.c│
│ │om │
├───────────────────────────────────┴───────────────────────────────────┤
│mailto:firstname.lastname@example.com │
└───────────────────────────────────────────────────────────────────────┘
With all excessive white spaces being removed, conditional line breaks in a cell need to be done using a markup. The implementation recognizes the two HTML line break markups <br>
and <br />
.
┌────────────────────────────────────────────────┐
│line 1 │
│line 2 │
│line three still line three │
└────────────────────────────────────────────────┘
┌────────────────────┬─────────────────────────┐
│column with a list │* list item one │
│using conditional │* list item two │
│line breaks │* list item three │
└────────────────────┴─────────────────────────┘
Left column w/o and right column with LaTeX target converter:
┌───────────────────────────────────────┬──────────────────────────────────────┐
│A sentence with some normal text, not │A sentence with some normal text, not │
│specific to LaTeX. Now for some │specific to LaTeX. Now for some │
│characters that require conversion: # %│characters that require conversion: \#│
│&. And some more: © § ¤. And even more:│\% \&. And some more: {\copyright} │
│È É Ê Ë. And some arrows as well: ← ↑ →│{\S} \currency. And even more: \`{E} │
│↓ ↔ │\'{E} \^{E} \"{E}. And some arrows as │
│ │well: \(\leftarrow{}\) \(\uparrow\) │
│ │\(\rightarrow{}\) \(\downarrow{}\) │
│ │\(\leftrightarrow{}\) │
└───────────────────────────────────────┴──────────────────────────────────────┘
Left column w/o and right column with HTML target converter
┌───────────────────────────────────────┬──────────────────────────────────────┐
│A sentence with some normal text, not │A sentence with some normal text, not │
│specific to HTML. Now for some │specific to HTML. Now for some │
│characters that require conversion: # %│characters that require conversion: │
│& < >. And some more: © § ¤. And even │̣ % & < >. And some│
│more: Ē ē Ĕ ĕ Ė ė Ę ę Ě ě. And some │more: © § ¤. And even│
│arrows as well: ← ↑ → ↓ ↔ ↕ │more: Ē ē Ĕ ĕ │
│ │Ė ė Ę ę Ě │
│ │ě. And some arrows as well: │
│ │← ↑ → ↓ ↔ │
│ │↕ │
└───────────────────────────────────────┴──────────────────────────────────────┘
Download Details:
Author: vdmeer
Source Code: https://github.com/vdmeer/asciitable
License: Apache-2.0 license
1650870267
In the previous chapters you've learnt how to select individual elements on a web page. But there are many occasions where you need to access a child, parent or ancestor element. See the JavaScript DOM nodes chapter to understand the logical relationships between the nodes in a DOM tree.
DOM node provides several properties and methods that allow you to navigate or traverse through the tree structure of the DOM and make changes very easily. In the following section we will learn how to navigate up, down, and sideways in the DOM tree using JavaScript.
You can use the firstChild
and lastChild
properties of the DOM node to access the first and last direct child node of a node, respectively. If the node doesn't have any child element, it returns null
.
<div id="main">
<h1 id="title">My Heading</h1>
<p id="hint"><span>This is some text.</span></p>
</div>
<script>
var main = document.getElementById("main");
console.log(main.firstChild.nodeName); // Prints: #text
var hint = document.getElementById("hint");
console.log(hint.firstChild.nodeName); // Prints: SPAN
</script>
Note: The
nodeName
is a read-only property that returns the name of the current node as a string. For example, it returns the tag name for element node,#text
for text node,#comment
for comment node,#document
for document node, and so on.
If you notice the above example, the nodeName
of the first-child node of the main DIV element returns #text instead of H1. Because, whitespace such as spaces, tabs, newlines, etc. are valid characters and they form #text nodes and become a part of the DOM tree. Therefore, since the <div>
tag contains a newline before the <h1>
tag, so it will create a #text node.
To avoid the issue with firstChild
and lastChild
returning #text or #comment nodes, you could alternatively use the firstElementChild
and lastElementChild
properties to return only the first and last element node, respectively. But, it will not work in IE 9 and earlier.
<div id="main">
<h1 id="title">My Heading</h1>
<p id="hint"><span>This is some text.</span></p>
</div>
<script>
var main = document.getElementById("main");
alert(main.firstElementChild.nodeName); // Outputs: H1
main.firstElementChild.style.color = "red";
var hint = document.getElementById("hint");
alert(hint.firstElementChild.nodeName); // Outputs: SPAN
hint.firstElementChild.style.color = "blue";
</script>
Similarly, you can use the childNodes
property to access all child nodes of a given element, where the first child node is assigned index 0. Here's an example:
<div id="main">
<h1 id="title">My Heading</h1>
<p id="hint"><span>This is some text.</span></p>
</div>
<script>
var main = document.getElementById("main");
// First check that the element has child nodes
if(main.hasChildNodes()) {
var nodes = main.childNodes;
// Loop through node list and display node name
for(var i = 0; i < nodes.length; i++) {
alert(nodes[i].nodeName);
}
}
</script>
The childNodes
returns all child nodes, including non-element nodes like text and comment nodes. To get a collection of only elements, use children
property instead.
<div id="main">
<h1 id="title">My Heading</h1>
<p id="hint"><span>This is some text.</span></p>
</div>
<script>
var main = document.getElementById("main");
// First check that the element has child nodes
if(main.hasChildNodes()) {
var nodes = main.children;
// Loop through node list and display node name
for(var i = 0; i < nodes.length; i++) {
alert(nodes[i].nodeName);
}
}
</script>
1646025910
Khám phá tập dữ liệu tin tức giả, thực hiện phân tích dữ liệu chẳng hạn như đám mây từ và ngram, đồng thời tinh chỉnh máy biến áp BERT để xây dựng bộ phát hiện tin tức giả bằng Python bằng cách sử dụng thư viện máy biến áp.
Tin tức giả là việc cố ý phát đi các tuyên bố sai sự thật hoặc gây hiểu lầm như một tin tức, trong đó các tuyên bố là cố ý lừa dối.
Báo chí, báo lá cải và tạp chí đã được thay thế bởi các nền tảng tin tức kỹ thuật số, blog, nguồn cấp dữ liệu truyền thông xã hội và rất nhiều ứng dụng tin tức di động. Các tổ chức tin tức được hưởng lợi từ việc tăng cường sử dụng mạng xã hội và các nền tảng di động bằng cách cung cấp cho người đăng ký thông tin cập nhật từng phút.
Người tiêu dùng hiện có thể truy cập ngay vào những tin tức mới nhất. Các nền tảng truyền thông kỹ thuật số này ngày càng nổi tiếng do khả năng kết nối dễ dàng với phần còn lại của thế giới và cho phép người dùng thảo luận, chia sẻ ý tưởng và tranh luận về các chủ đề như dân chủ, giáo dục, y tế, nghiên cứu và lịch sử. Các mục tin tức giả mạo trên các nền tảng kỹ thuật số ngày càng phổ biến và được sử dụng để thu lợi nhuận, chẳng hạn như lợi ích chính trị và tài chính.
Bởi vì Internet, phương tiện truyền thông xã hội và các nền tảng kỹ thuật số được sử dụng rộng rãi, bất kỳ ai cũng có thể tuyên truyền thông tin không chính xác và thiên vị. Gần như không thể ngăn chặn sự lan truyền của tin tức giả mạo. Có một sự gia tăng đáng kể trong việc phát tán tin tức sai lệch, không chỉ giới hạn trong một lĩnh vực như chính trị mà bao gồm thể thao, sức khỏe, lịch sử, giải trí, khoa học và nghiên cứu.
Điều quan trọng là phải nhận biết và phân biệt giữa tin tức sai và tin tức chính xác. Một phương pháp là nhờ một chuyên gia quyết định và kiểm tra thực tế mọi thông tin, nhưng điều này cần thời gian và cần chuyên môn không thể chia sẻ được. Thứ hai, chúng ta có thể sử dụng các công cụ học máy và trí tuệ nhân tạo để tự động hóa việc xác định tin tức giả mạo.
Thông tin tin tức trực tuyến bao gồm nhiều dữ liệu định dạng phi cấu trúc khác nhau (chẳng hạn như tài liệu, video và âm thanh), nhưng chúng tôi sẽ tập trung vào tin tức định dạng văn bản ở đây. Với tiến bộ của học máy và xử lý ngôn ngữ tự nhiên , giờ đây chúng ta có thể nhận ra đặc điểm gây hiểu lầm và sai của một bài báo hoặc câu lệnh.
Một số nghiên cứu và thử nghiệm đang được tiến hành để phát hiện tin tức giả trên tất cả các phương tiện.
Mục tiêu chính của chúng tôi trong hướng dẫn này là:
Đây là bảng nội dung:
Trong công việc này, chúng tôi đã sử dụng tập dữ liệu tin tức giả từ Kaggle để phân loại các bài báo không đáng tin cậy là tin giả. Chúng tôi có một tập dữ liệu đào tạo hoàn chỉnh chứa các đặc điểm sau:
id
: id duy nhất cho một bài báotitle
: tiêu đề của một bài báoauthor
: tác giả của bài báotext
: văn bản của bài báo; có thể không đầy đủlabel
: nhãn đánh dấu bài viết có khả năng không đáng tin cậy được ký hiệu bằng 1 (không đáng tin cậy hoặc giả mạo) hoặc 0 (đáng tin cậy).Đó là một bài toán phân loại nhị phân, trong đó chúng ta phải dự đoán xem một câu chuyện tin tức cụ thể có đáng tin cậy hay không.
Nếu bạn có tài khoản Kaggle, bạn có thể chỉ cần tải xuống bộ dữ liệu từ trang web ở đó và giải nén tệp ZIP.
Tôi cũng đã tải tập dữ liệu lên Google Drive và bạn có thể tải tập dữ liệu đó tại đây hoặc sử dụng gdown
thư viện để tự động tải xuống tập dữ liệu trong sổ ghi chép Google Colab hoặc Jupyter:
$ pip install gdown
# download from Google Drive
$ gdown "https://drive.google.com/uc?id=178f_VkNxccNidap-5-uffXUW475pAuPy&confirm=t"
Downloading...
From: https://drive.google.com/uc?id=178f_VkNxccNidap-5-uffXUW475pAuPy&confirm=t
To: /content/fake-news.zip
100% 48.7M/48.7M [00:00<00:00, 74.6MB/s]
Giải nén các tệp:
$ unzip fake-news.zip
Ba tệp sẽ xuất hiện trong thư mục làm việc hiện tại:, và train.csv
, chúng tôi sẽ sử dụng trong hầu hết các hướng dẫn.test.csvsubmit.csvtrain.csv
Cài đặt các phụ thuộc bắt buộc:
$ pip install transformers nltk pandas numpy matplotlib seaborn wordcloud
Lưu ý: Nếu bạn đang ở trong môi trường cục bộ, hãy đảm bảo rằng bạn cài đặt PyTorch cho GPU, hãy truy cập trang này để cài đặt đúng cách.
Hãy nhập các thư viện cần thiết để phân tích:
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
Kho tập tin NLTK và mô-đun phải được cài đặt bằng trình tải xuống NLTK tiêu chuẩn:
import nltk
nltk.download('stopwords')
nltk.download('wordnet')
Tập dữ liệu tin tức giả bao gồm các tiêu đề và văn bản bài báo gốc và hư cấu của nhiều tác giả khác nhau. Hãy nhập tập dữ liệu của chúng tôi:
# load the dataset
news_d = pd.read_csv("train.csv")
print("Shape of News data:", news_d.shape)
print("News data columns", news_d.columns)
Đầu ra:
Shape of News data: (20800, 5)
News data columns Index(['id', 'title', 'author', 'text', 'label'], dtype='object')
Đây là giao diện của tập dữ liệu:
# by using df.head(), we can immediately familiarize ourselves with the dataset.
news_d.head()
Đầu ra:
id title author text label
0 0 House Dem Aide: We Didn’t Even See Comey’s Let... Darrell Lucus House Dem Aide: We Didn’t Even See Comey’s Let... 1
1 1 FLYNN: Hillary Clinton, Big Woman on Campus - ... Daniel J. Flynn Ever get the feeling your life circles the rou... 0
2 2 Why the Truth Might Get You Fired Consortiumnews.com Why the Truth Might Get You Fired October 29, ... 1
3 3 15 Civilians Killed In Single US Airstrike Hav... Jessica Purkiss Videos 15 Civilians Killed In Single US Airstr... 1
4 4 Iranian woman jailed for fictional unpublished... Howard Portnoy Print \nAn Iranian woman has been sentenced to... 1
Chúng tôi có 20.800 hàng, có năm cột. Hãy cùng xem một số thống kê của chuyên text
mục:
#Text Word startistics: min.mean, max and interquartile range
txt_length = news_d.text.str.split().str.len()
txt_length.describe()
Đầu ra:
count 20761.000000
mean 760.308126
std 869.525988
min 0.000000
25% 269.000000
50% 556.000000
75% 1052.000000
max 24234.000000
Name: text, dtype: float64
Số liệu thống kê cho title
cột:
#Title statistics
title_length = news_d.title.str.split().str.len()
title_length.describe()
Đầu ra:
count 20242.000000
mean 12.420709
std 4.098735
min 1.000000
25% 10.000000
50% 13.000000
75% 15.000000
max 72.000000
Name: title, dtype: float64
Số liệu thống kê cho các tập huấn luyện và kiểm tra như sau:
text
tính có số từ cao hơn với trung bình 760 từ và 75% có hơn 1000 từ.title
tính là một câu lệnh ngắn với trung bình 12 từ và 75% trong số đó là khoảng 15 từ.Thử nghiệm của chúng tôi sẽ kết hợp cả văn bản và tiêu đề.
Đếm các ô cho cả hai nhãn:
sns.countplot(x="label", data=news_d);
print("1: Unreliable")
print("0: Reliable")
print("Distribution of labels:")
print(news_d.label.value_counts());
Đầu ra:
1: Unreliable
0: Reliable
Distribution of labels:
1 10413
0 10387
Name: label, dtype: int64
print(round(news_d.label.value_counts(normalize=True),2)*100);
Đầu ra:
1 50.0
0 50.0
Name: label, dtype: float64
Số lượng bài báo không đáng tin cậy (giả mạo hoặc 1) là 10413, trong khi số bài báo đáng tin cậy (đáng tin cậy hoặc 0) là 10387. Gần 50% số bài báo là giả mạo. Do đó, chỉ số độ chính xác sẽ đo lường mức độ hoạt động của mô hình của chúng tôi khi xây dựng bộ phân loại.
Trong phần này, chúng tôi sẽ làm sạch tập dữ liệu của mình để thực hiện một số phân tích:
# Constants that are used to sanitize the datasets
column_n = ['id', 'title', 'author', 'text', 'label']
remove_c = ['id','author']
categorical_features = []
target_col = ['label']
text_f = ['title', 'text']
# Clean Datasets
import nltk
from nltk.corpus import stopwords
import re
from nltk.stem.porter import PorterStemmer
from collections import Counter
ps = PorterStemmer()
wnl = nltk.stem.WordNetLemmatizer()
stop_words = stopwords.words('english')
stopwords_dict = Counter(stop_words)
# Removed unused clumns
def remove_unused_c(df,column_n=remove_c):
df = df.drop(column_n,axis=1)
return df
# Impute null values with None
def null_process(feature_df):
for col in text_f:
feature_df.loc[feature_df[col].isnull(), col] = "None"
return feature_df
def clean_dataset(df):
# remove unused column
df = remove_unused_c(df)
#impute null values
df = null_process(df)
return df
# Cleaning text from unused characters
def clean_text(text):
text = str(text).replace(r'http[\w:/\.]+', ' ') # removing urls
text = str(text).replace(r'[^\.\w\s]', ' ') # remove everything but characters and punctuation
text = str(text).replace('[^a-zA-Z]', ' ')
text = str(text).replace(r'\s\s+', ' ')
text = text.lower().strip()
#text = ' '.join(text)
return text
## Nltk Preprocessing include:
# Stop words, Stemming and Lemmetization
# For our project we use only Stop word removal
def nltk_preprocess(text):
text = clean_text(text)
wordlist = re.sub(r'[^\w\s]', '', text).split()
#text = ' '.join([word for word in wordlist if word not in stopwords_dict])
#text = [ps.stem(word) for word in wordlist if not word in stopwords_dict]
text = ' '.join([wnl.lemmatize(word) for word in wordlist if word not in stopwords_dict])
return text
Trong khối mã trên:
re
cho regex.nltk.corpus
. Khi làm việc với các từ, đặc biệt là khi xem xét ngữ nghĩa, đôi khi chúng ta cần loại bỏ các từ phổ biến không bổ sung bất kỳ ý nghĩa quan trọng nào cho một câu lệnh, chẳng hạn như "but"
,, v.v."can""we"
PorterStemmer
được sử dụng để thực hiện các từ gốc với NLTK. Các gốc từ loại bỏ các phụ tố hình thái của các từ, chỉ để lại phần gốc của từ.WordNetLemmatizer()
từ thư viện NLTK để lemmatization. Lemmatization hiệu quả hơn nhiều so với việc chiết cành . Nó vượt ra ngoài việc rút gọn từ và đánh giá toàn bộ từ vựng của một ngôn ngữ để áp dụng phân tích hình thái học cho các từ, với mục tiêu chỉ loại bỏ các kết thúc không theo chiều hướng và trả lại dạng cơ sở hoặc dạng từ điển của một từ, được gọi là bổ đề.stopwords.words('english')
cho phép chúng tôi xem danh sách tất cả các từ dừng tiếng Anh được NLTK hỗ trợ.remove_unused_c()
được sử dụng để loại bỏ các cột không sử dụng.None
cách sử dụng null_process()
hàm.clean_dataset()
, chúng ta gọi remove_unused_c()
và null_process()
hàm. Chức năng này có nhiệm vụ làm sạch dữ liệu.clean_text()
hàm.nltk_preprocess()
chức năng cho mục đích đó.Tiền xử lý text
và title
:
# Perform data cleaning on train and test dataset by calling clean_dataset function
df = clean_dataset(news_d)
# apply preprocessing on text through apply method by calling the function nltk_preprocess
df["text"] = df.text.apply(nltk_preprocess)
# apply preprocessing on title through apply method by calling the function nltk_preprocess
df["title"] = df.title.apply(nltk_preprocess)
# Dataset after cleaning and preprocessing step
df.head()
Đầu ra:
title text label
0 house dem aide didnt even see comeys letter ja... house dem aide didnt even see comeys letter ja... 1
1 flynn hillary clinton big woman campus breitbart ever get feeling life circle roundabout rather... 0
2 truth might get fired truth might get fired october 29 2016 tension ... 1
3 15 civilian killed single u airstrike identified video 15 civilian killed single u airstrike id... 1
4 iranian woman jailed fictional unpublished sto... print iranian woman sentenced six year prison ... 1
Trong phần này, chúng tôi sẽ thực hiện:
Các từ phổ biến nhất xuất hiện ở phông chữ đậm và lớn hơn trong đám mây từ. Phần này sẽ thực hiện một đám mây từ cho tất cả các từ trong tập dữ liệu.
Chức năng của thư viện WordCloudwordcloud()
sẽ được sử dụng và generate()
được sử dụng để tạo hình ảnh đám mây từ:
from wordcloud import WordCloud, STOPWORDS
import matplotlib.pyplot as plt
# initialize the word cloud
wordcloud = WordCloud( background_color='black', width=800, height=600)
# generate the word cloud by passing the corpus
text_cloud = wordcloud.generate(' '.join(df['text']))
# plotting the word cloud
plt.figure(figsize=(20,30))
plt.imshow(text_cloud)
plt.axis('off')
plt.show()
Đầu ra:
Đám mây từ chỉ dành cho tin tức đáng tin cậy:
true_n = ' '.join(df[df['label']==0]['text'])
wc = wordcloud.generate(true_n)
plt.figure(figsize=(20,30))
plt.imshow(wc)
plt.axis('off')
plt.show()
Đầu ra:
Word cloud chỉ dành cho tin tức giả mạo:
fake_n = ' '.join(df[df['label']==1]['text'])
wc= wordcloud.generate(fake_n)
plt.figure(figsize=(20,30))
plt.imshow(wc)
plt.axis('off')
plt.show()
Đầu ra:
N-gram là một chuỗi các chữ cái hoặc từ. Một ký tự unigram được tạo thành từ một ký tự duy nhất, trong khi một bigram bao gồm một chuỗi hai ký tự. Tương tự, từ N-gram được tạo thành từ một chuỗi n từ. Từ "thống nhất" là 1 gam (unigram). Sự kết hợp của các từ "bang thống nhất" là 2 gam (bigram), "thành phố new york" là 3 gam.
Hãy vẽ biểu đồ phổ biến nhất trên tin tức đáng tin cậy:
def plot_top_ngrams(corpus, title, ylabel, xlabel="Number of Occurences", n=2):
"""Utility function to plot top n-grams"""
true_b = (pd.Series(nltk.ngrams(corpus.split(), n)).value_counts())[:20]
true_b.sort_values().plot.barh(color='blue', width=.9, figsize=(12, 8))
plt.title(title)
plt.ylabel(ylabel)
plt.xlabel(xlabel)
plt.show()
plot_top_ngrams(true_n, 'Top 20 Frequently Occuring True news Bigrams', "Bigram", n=2)
Biểu đồ phổ biến nhất về tin tức giả:
plot_top_ngrams(fake_n, 'Top 20 Frequently Occuring Fake news Bigrams', "Bigram", n=2)
Hình bát quái phổ biến nhất trên các tin tức đáng tin cậy:
plot_top_ngrams(true_n, 'Top 20 Frequently Occuring True news Trigrams', "Trigrams", n=3)
Đối với tin tức giả mạo bây giờ:
plot_top_ngrams(fake_n, 'Top 20 Frequently Occuring Fake news Trigrams', "Trigrams", n=3)
Các biểu đồ trên cho chúng ta một số ý tưởng về giao diện của cả hai lớp. Trong phần tiếp theo, chúng ta sẽ sử dụng thư viện máy biến áp để xây dựng công cụ phát hiện tin tức giả.
Phần này sẽ lấy mã rộng rãi từ hướng dẫn tinh chỉnh BERT để tạo bộ phân loại tin tức giả bằng cách sử dụng thư viện máy biến áp. Vì vậy, để biết thêm thông tin chi tiết, bạn có thể xem hướng dẫn ban đầu .
Nếu bạn không cài đặt máy biến áp, bạn phải:
$ pip install transformers
Hãy nhập các thư viện cần thiết:
import torch
from transformers.file_utils import is_tf_available, is_torch_available, is_torch_tpu_available
from transformers import BertTokenizerFast, BertForSequenceClassification
from transformers import Trainer, TrainingArguments
import numpy as np
from sklearn.model_selection import train_test_split
import random
Chúng tôi muốn làm cho kết quả của chúng tôi có thể tái tạo ngay cả khi chúng tôi khởi động lại môi trường của mình:
def set_seed(seed: int):
"""
Helper function for reproducible behavior to set the seed in ``random``, ``numpy``, ``torch`` and/or ``tf`` (if
installed).
Args:
seed (:obj:`int`): The seed to set.
"""
random.seed(seed)
np.random.seed(seed)
if is_torch_available():
torch.manual_seed(seed)
torch.cuda.manual_seed_all(seed)
# ^^ safe to call this function even if cuda is not available
if is_tf_available():
import tensorflow as tf
tf.random.set_seed(seed)
set_seed(1)
Mô hình chúng tôi sẽ sử dụng là bert-base-uncased
:
# the model we gonna train, base uncased BERT
# check text classification models here: https://huggingface.co/models?filter=text-classification
model_name = "bert-base-uncased"
# max sequence length for each document/sentence sample
max_length = 512
Đang tải tokenizer:
# load the tokenizer
tokenizer = BertTokenizerFast.from_pretrained(model_name, do_lower_case=True)
Bây giờ chúng ta hãy làm sạch NaN
các giá trị khỏi text
và author
các title
cột:
news_df = news_d[news_d['text'].notna()]
news_df = news_df[news_df["author"].notna()]
news_df = news_df[news_df["title"].notna()]
Tiếp theo, tạo một hàm lấy tập dữ liệu làm khung dữ liệu Pandas và trả về phần tách dòng / xác thực của văn bản và nhãn dưới dạng danh sách:
def prepare_data(df, test_size=0.2, include_title=True, include_author=True):
texts = []
labels = []
for i in range(len(df)):
text = df["text"].iloc[i]
label = df["label"].iloc[i]
if include_title:
text = df["title"].iloc[i] + " - " + text
if include_author:
text = df["author"].iloc[i] + " : " + text
if text and label in [0, 1]:
texts.append(text)
labels.append(label)
return train_test_split(texts, labels, test_size=test_size)
train_texts, valid_texts, train_labels, valid_labels = prepare_data(news_df)
Hàm trên nhận tập dữ liệu trong một kiểu khung dữ liệu và trả về chúng dưới dạng danh sách được chia thành các tập hợp lệ và huấn luyện. Đặt include_title
thành True
có nghĩa là chúng tôi thêm title
cột vào mục text
chúng tôi sẽ sử dụng để đào tạo, đặt include_author
thành True
có nghĩa là chúng tôi cũng thêm author
vào văn bản.
Hãy đảm bảo rằng các nhãn và văn bản có cùng độ dài:
print(len(train_texts), len(train_labels))
print(len(valid_texts), len(valid_labels))
Đầu ra:
14628 14628
3657 3657
Hãy sử dụng trình mã hóa BERT để mã hóa tập dữ liệu của chúng ta:
# tokenize the dataset, truncate when passed `max_length`,
# and pad with 0's when less than `max_length`
train_encodings = tokenizer(train_texts, truncation=True, padding=True, max_length=max_length)
valid_encodings = tokenizer(valid_texts, truncation=True, padding=True, max_length=max_length)
Chuyển đổi các mã hóa thành tập dữ liệu PyTorch:
class NewsGroupsDataset(torch.utils.data.Dataset):
def __init__(self, encodings, labels):
self.encodings = encodings
self.labels = labels
def __getitem__(self, idx):
item = {k: torch.tensor(v[idx]) for k, v in self.encodings.items()}
item["labels"] = torch.tensor([self.labels[idx]])
return item
def __len__(self):
return len(self.labels)
# convert our tokenized data into a torch Dataset
train_dataset = NewsGroupsDataset(train_encodings, train_labels)
valid_dataset = NewsGroupsDataset(valid_encodings, valid_labels)
Chúng tôi sẽ sử dụng BertForSequenceClassification
để tải mô hình máy biến áp BERT của chúng tôi:
# load the model
model = BertForSequenceClassification.from_pretrained(model_name, num_labels=2)
Chúng tôi đặt num_labels
thành 2 vì đó là phân loại nhị phân. Hàm dưới đây là một lệnh gọi lại để tính độ chính xác trên mỗi bước xác thực:
from sklearn.metrics import accuracy_score
def compute_metrics(pred):
labels = pred.label_ids
preds = pred.predictions.argmax(-1)
# calculate accuracy using sklearn's function
acc = accuracy_score(labels, preds)
return {
'accuracy': acc,
}
Hãy khởi tạo các tham số huấn luyện:
training_args = TrainingArguments(
output_dir='./results', # output directory
num_train_epochs=1, # total number of training epochs
per_device_train_batch_size=10, # batch size per device during training
per_device_eval_batch_size=20, # batch size for evaluation
warmup_steps=100, # number of warmup steps for learning rate scheduler
logging_dir='./logs', # directory for storing logs
load_best_model_at_end=True, # load the best model when finished training (default metric is loss)
# but you can specify `metric_for_best_model` argument to change to accuracy or other metric
logging_steps=200, # log & save weights each logging_steps
save_steps=200,
evaluation_strategy="steps", # evaluate each `logging_steps`
)
Tôi đã đặt thành per_device_train_batch_size
10, nhưng bạn nên đặt nó cao nhất có thể phù hợp với GPU của bạn. Đặt logging_steps
và save_steps
thành 200, nghĩa là chúng ta sẽ thực hiện đánh giá và lưu trọng số của mô hình trên mỗi 200 bước huấn luyện.
Bạn có thể kiểm tra trang này để biết thêm thông tin chi tiết về các thông số đào tạo có sẵn.
Hãy khởi tạo trình huấn luyện:
trainer = Trainer(
model=model, # the instantiated Transformers model to be trained
args=training_args, # training arguments, defined above
train_dataset=train_dataset, # training dataset
eval_dataset=valid_dataset, # evaluation dataset
compute_metrics=compute_metrics, # the callback that computes metrics of interest
)
Đào tạo người mẫu:
# train the model
trainer.train()
Quá trình đào tạo mất vài giờ để kết thúc, tùy thuộc vào GPU của bạn. Nếu bạn đang sử dụng phiên bản Colab miễn phí, sẽ mất một giờ với NVIDIA Tesla K80. Đây là kết quả:
***** Running training *****
Num examples = 14628
Num Epochs = 1
Instantaneous batch size per device = 10
Total train batch size (w. parallel, distributed & accumulation) = 10
Gradient Accumulation steps = 1
Total optimization steps = 1463
[1463/1463 41:07, Epoch 1/1]
Step Training Loss Validation Loss Accuracy
200 0.250800 0.100533 0.983867
400 0.027600 0.043009 0.993437
600 0.023400 0.017812 0.997539
800 0.014900 0.030269 0.994258
1000 0.022400 0.012961 0.998086
1200 0.009800 0.010561 0.998633
1400 0.007700 0.010300 0.998633
***** Running Evaluation *****
Num examples = 3657
Batch size = 20
Saving model checkpoint to ./results/checkpoint-200
Configuration saved in ./results/checkpoint-200/config.json
Model weights saved in ./results/checkpoint-200/pytorch_model.bin
<SNIPPED>
***** Running Evaluation *****
Num examples = 3657
Batch size = 20
Saving model checkpoint to ./results/checkpoint-1400
Configuration saved in ./results/checkpoint-1400/config.json
Model weights saved in ./results/checkpoint-1400/pytorch_model.bin
Training completed. Do not forget to share your model on huggingface.co/models =)
Loading best model from ./results/checkpoint-1400 (score: 0.010299865156412125).
TrainOutput(global_step=1463, training_loss=0.04888018785440506, metrics={'train_runtime': 2469.1722, 'train_samples_per_second': 5.924, 'train_steps_per_second': 0.593, 'total_flos': 3848788517806080.0, 'train_loss': 0.04888018785440506, 'epoch': 1.0})
Vì load_best_model_at_end
được đặt thành True
, mức tạ tốt nhất sẽ được tải khi quá trình tập luyện hoàn thành. Hãy đánh giá nó với bộ xác thực của chúng tôi:
# evaluate the current model after training
trainer.evaluate()
Đầu ra:
***** Running Evaluation *****
Num examples = 3657
Batch size = 20
[183/183 02:11]
{'epoch': 1.0,
'eval_accuracy': 0.998632759092152,
'eval_loss': 0.010299865156412125,
'eval_runtime': 132.0374,
'eval_samples_per_second': 27.697,
'eval_steps_per_second': 1.386}
Lưu mô hình và tokenizer:
# saving the fine tuned model & tokenizer
model_path = "fake-news-bert-base-uncased"
model.save_pretrained(model_path)
tokenizer.save_pretrained(model_path)
Một thư mục mới chứa cấu hình mô hình và trọng số sẽ xuất hiện sau khi chạy ô trên. Nếu bạn muốn thực hiện dự đoán, bạn chỉ cần sử dụng from_pretrained()
phương pháp chúng tôi đã sử dụng khi tải mô hình và bạn đã sẵn sàng.
Tiếp theo, hãy tạo một hàm chấp nhận văn bản bài viết làm đối số và trả về cho dù nó là giả mạo hay không:
def get_prediction(text, convert_to_label=False):
# prepare our text into tokenized sequence
inputs = tokenizer(text, padding=True, truncation=True, max_length=max_length, return_tensors="pt").to("cuda")
# perform inference to our model
outputs = model(**inputs)
# get output probabilities by doing softmax
probs = outputs[0].softmax(1)
# executing argmax function to get the candidate label
d = {
0: "reliable",
1: "fake"
}
if convert_to_label:
return d[int(probs.argmax())]
else:
return int(probs.argmax())
Tôi đã lấy một ví dụ từ test.csv
mô hình chưa từng thấy để thực hiện suy luận, tôi đã kiểm tra nó và đó là một bài báo thực tế từ The New York Times:
real_news = """
Tim Tebow Will Attempt Another Comeback, This Time in Baseball - The New York Times",Daniel Victor,"If at first you don’t succeed, try a different sport. Tim Tebow, who was a Heisman quarterback at the University of Florida but was unable to hold an N. F. L. job, is pursuing a career in Major League Baseball. <SNIPPED>
"""
Văn bản gốc nằm trong môi trường Colab nếu bạn muốn sao chép nó, vì nó là một bài báo hoàn chỉnh. Hãy chuyển nó cho mô hình và xem kết quả:
get_prediction(real_news, convert_to_label=True)
Đầu ra:
reliable
Trong phần này, chúng tôi sẽ dự đoán tất cả các bài trong phần test.csv
để tạo hồ sơ gửi để xem độ chính xác của chúng tôi trong bộ bài kiểm tra của cuộc thi Kaggle :
# read the test set
test_df = pd.read_csv("test.csv")
# make a copy of the testing set
new_df = test_df.copy()
# add a new column that contains the author, title and article content
new_df["new_text"] = new_df["author"].astype(str) + " : " + new_df["title"].astype(str) + " - " + new_df["text"].astype(str)
# get the prediction of all the test set
new_df["label"] = new_df["new_text"].apply(get_prediction)
# make the submission file
final_df = new_df[["id", "label"]]
final_df.to_csv("submit_final.csv", index=False)
Sau khi chúng tôi nối tác giả, tiêu đề và văn bản bài viết với nhau, chúng tôi truyền get_prediction()
hàm vào cột mới để lấp đầy label
cột, sau đó chúng tôi sử dụng to_csv()
phương thức để tạo tệp gửi cho Kaggle. Đây là điểm nộp bài của tôi:
Chúng tôi nhận được độ chính xác 99,78% và 100% trên bảng xếp hạng riêng tư và công khai. Thật tuyệt vời!
Được rồi, chúng ta đã hoàn thành phần hướng dẫn. Bạn có thể kiểm tra trang này để xem các thông số đào tạo khác nhau mà bạn có thể điều chỉnh.
Nếu bạn có tập dữ liệu tin tức giả tùy chỉnh để tinh chỉnh, bạn chỉ cần chuyển danh sách các mẫu cho trình mã hóa như chúng tôi đã làm, bạn sẽ không thay đổi bất kỳ mã nào khác sau đó.
Kiểm tra mã hoàn chỉnh tại đây hoặc môi trường Colab tại đây .
1646055360
Explorar el conjunto de datos de noticias falsas, realizar análisis de datos como nubes de palabras y ngramas, y ajustar el transformador BERT para construir un detector de noticias falsas en Python usando la biblioteca de transformadores.
Las noticias falsas son la transmisión intencional de afirmaciones falsas o engañosas como noticias, donde las declaraciones son deliberadamente engañosas.
Los periódicos, tabloides y revistas han sido reemplazados por plataformas de noticias digitales, blogs, fuentes de redes sociales y una plétora de aplicaciones de noticias móviles. Las organizaciones de noticias se beneficiaron del mayor uso de las redes sociales y las plataformas móviles al proporcionar a los suscriptores información actualizada al minuto.
Los consumidores ahora tienen acceso instantáneo a las últimas noticias. Estas plataformas de medios digitales han aumentado en importancia debido a su fácil conexión con el resto del mundo y permiten a los usuarios discutir y compartir ideas y debatir temas como la democracia, la educación, la salud, la investigación y la historia. Las noticias falsas en las plataformas digitales son cada vez más populares y se utilizan con fines de lucro, como ganancias políticas y financieras.
Debido a que Internet, las redes sociales y las plataformas digitales son ampliamente utilizadas, cualquiera puede propagar información inexacta y sesgada. Es casi imposible evitar la difusión de noticias falsas. Hay un aumento tremendo en la distribución de noticias falsas, que no se restringe a un sector como la política sino que incluye deportes, salud, historia, entretenimiento y ciencia e investigación.
Es vital reconocer y diferenciar entre noticias falsas y veraces. Un método es hacer que un experto decida y verifique cada pieza de información, pero esto lleva tiempo y requiere experiencia que no se puede compartir. En segundo lugar, podemos utilizar herramientas de aprendizaje automático e inteligencia artificial para automatizar la identificación de noticias falsas.
La información de noticias en línea incluye varios datos en formato no estructurado (como documentos, videos y audio), pero aquí nos concentraremos en las noticias en formato de texto. Con el progreso del aprendizaje automático y el procesamiento del lenguaje natural , ahora podemos reconocer el carácter engañoso y falso de un artículo o declaración.
Se están realizando varios estudios y experimentos para detectar noticias falsas en todos los medios.
Nuestro objetivo principal de este tutorial es:
Aquí está la tabla de contenido:
En este trabajo, utilizamos el conjunto de datos de noticias falsas de Kaggle para clasificar artículos de noticias no confiables como noticias falsas. Disponemos de un completo dataset de entrenamiento que contiene las siguientes características:
id
: identificación única para un artículo de noticiastitle
: título de un artículo periodísticoauthor
: autor de la noticiatext
: texto del artículo; podría estar incompletolabel
: una etiqueta que marca el artículo como potencialmente no confiable denotado por 1 (poco confiable o falso) o 0 (confiable).Es un problema de clasificación binaria en el que debemos predecir si una determinada noticia es fiable o no.
Si tiene una cuenta de Kaggle, simplemente puede descargar el conjunto de datos del sitio web y extraer el archivo ZIP.
También cargué el conjunto de datos en Google Drive y puede obtenerlo aquí o usar la gdown
biblioteca para descargarlo automáticamente en Google Colab o cuadernos de Jupyter:
$ pip install gdown
# download from Google Drive
$ gdown "https://drive.google.com/uc?id=178f_VkNxccNidap-5-uffXUW475pAuPy&confirm=t"
Downloading...
From: https://drive.google.com/uc?id=178f_VkNxccNidap-5-uffXUW475pAuPy&confirm=t
To: /content/fake-news.zip
100% 48.7M/48.7M [00:00<00:00, 74.6MB/s]
Descomprimiendo los archivos:
$ unzip fake-news.zip
Aparecerán tres archivos en el directorio de trabajo actual: train.csv
, test.csv
y submit.csv
, que usaremos train.csv
en la mayor parte del tutorial.
Instalando las dependencias requeridas:
$ pip install transformers nltk pandas numpy matplotlib seaborn wordcloud
Nota: si se encuentra en un entorno local, asegúrese de instalar PyTorch para GPU, diríjase a esta página para una instalación adecuada.
Importemos las bibliotecas esenciales para el análisis:
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
El corpus y los módulos NLTK deben instalarse mediante el descargador NLTK estándar:
import nltk
nltk.download('stopwords')
nltk.download('wordnet')
El conjunto de datos de noticias falsas comprende títulos y textos de artículos originales y ficticios de varios autores. Importemos nuestro conjunto de datos:
# load the dataset
news_d = pd.read_csv("train.csv")
print("Shape of News data:", news_d.shape)
print("News data columns", news_d.columns)
Producción:
Shape of News data: (20800, 5)
News data columns Index(['id', 'title', 'author', 'text', 'label'], dtype='object')
Así es como se ve el conjunto de datos:
# by using df.head(), we can immediately familiarize ourselves with the dataset.
news_d.head()
Producción:
id title author text label
0 0 House Dem Aide: We Didn’t Even See Comey’s Let... Darrell Lucus House Dem Aide: We Didn’t Even See Comey’s Let... 1
1 1 FLYNN: Hillary Clinton, Big Woman on Campus - ... Daniel J. Flynn Ever get the feeling your life circles the rou... 0
2 2 Why the Truth Might Get You Fired Consortiumnews.com Why the Truth Might Get You Fired October 29, ... 1
3 3 15 Civilians Killed In Single US Airstrike Hav... Jessica Purkiss Videos 15 Civilians Killed In Single US Airstr... 1
4 4 Iranian woman jailed for fictional unpublished... Howard Portnoy Print \nAn Iranian woman has been sentenced to... 1
Tenemos 20.800 filas, que tienen cinco columnas. Veamos algunas estadísticas de la text
columna:
#Text Word startistics: min.mean, max and interquartile range
txt_length = news_d.text.str.split().str.len()
txt_length.describe()
Producción:
count 20761.000000
mean 760.308126
std 869.525988
min 0.000000
25% 269.000000
50% 556.000000
75% 1052.000000
max 24234.000000
Name: text, dtype: float64
Estadísticas de la title
columna:
#Title statistics
title_length = news_d.title.str.split().str.len()
title_length.describe()
Producción:
count 20242.000000
mean 12.420709
std 4.098735
min 1.000000
25% 10.000000
50% 13.000000
75% 15.000000
max 72.000000
Name: title, dtype: float64
Las estadísticas para los conjuntos de entrenamiento y prueba son las siguientes:
text
atributo tiene un conteo de palabras más alto con un promedio de 760 palabras y un 75% con más de 1000 palabras.title
atributo es una declaración breve con un promedio de 12 palabras, y el 75% de ellas tiene alrededor de 15 palabras.Nuestro experimento sería con el texto y el título juntos.
Parcelas de conteo para ambas etiquetas:
sns.countplot(x="label", data=news_d);
print("1: Unreliable")
print("0: Reliable")
print("Distribution of labels:")
print(news_d.label.value_counts());
Producción:
1: Unreliable
0: Reliable
Distribution of labels:
1 10413
0 10387
Name: label, dtype: int64
print(round(news_d.label.value_counts(normalize=True),2)*100);
Producción:
1 50.0
0 50.0
Name: label, dtype: float64
La cantidad de artículos no confiables (falsos o 1) es 10413, mientras que la cantidad de artículos confiables (confiables o 0) es 10387. Casi el 50% de los artículos son falsos. Por lo tanto, la métrica de precisión medirá qué tan bien funciona nuestro modelo al construir un clasificador.
En esta sección, limpiaremos nuestro conjunto de datos para hacer algunos análisis:
# Constants that are used to sanitize the datasets
column_n = ['id', 'title', 'author', 'text', 'label']
remove_c = ['id','author']
categorical_features = []
target_col = ['label']
text_f = ['title', 'text']
# Clean Datasets
import nltk
from nltk.corpus import stopwords
import re
from nltk.stem.porter import PorterStemmer
from collections import Counter
ps = PorterStemmer()
wnl = nltk.stem.WordNetLemmatizer()
stop_words = stopwords.words('english')
stopwords_dict = Counter(stop_words)
# Removed unused clumns
def remove_unused_c(df,column_n=remove_c):
df = df.drop(column_n,axis=1)
return df
# Impute null values with None
def null_process(feature_df):
for col in text_f:
feature_df.loc[feature_df[col].isnull(), col] = "None"
return feature_df
def clean_dataset(df):
# remove unused column
df = remove_unused_c(df)
#impute null values
df = null_process(df)
return df
# Cleaning text from unused characters
def clean_text(text):
text = str(text).replace(r'http[\w:/\.]+', ' ') # removing urls
text = str(text).replace(r'[^\.\w\s]', ' ') # remove everything but characters and punctuation
text = str(text).replace('[^a-zA-Z]', ' ')
text = str(text).replace(r'\s\s+', ' ')
text = text.lower().strip()
#text = ' '.join(text)
return text
## Nltk Preprocessing include:
# Stop words, Stemming and Lemmetization
# For our project we use only Stop word removal
def nltk_preprocess(text):
text = clean_text(text)
wordlist = re.sub(r'[^\w\s]', '', text).split()
#text = ' '.join([word for word in wordlist if word not in stopwords_dict])
#text = [ps.stem(word) for word in wordlist if not word in stopwords_dict]
text = ' '.join([wnl.lemmatize(word) for word in wordlist if word not in stopwords_dict])
return text
En el bloque de código de arriba:
re
para expresiones regulares.nltk.corpus
. Cuando trabajamos con palabras, particularmente cuando consideramos la semántica, a veces necesitamos eliminar palabras comunes que no agregan ningún significado significativo a una declaración, como "but"
, "can"
, "we"
, etc.PorterStemmer
se utiliza para realizar palabras derivadas con NLTK. Los lematizadores despojan a las palabras de sus afijos morfológicos, dejando únicamente la raíz de la palabra.WordNetLemmatizer()
de la biblioteca NLTK para la lematización. La lematización es mucho más eficaz que la derivación . Va más allá de la reducción de palabras y evalúa todo el léxico de un idioma para aplicar el análisis morfológico a las palabras, con el objetivo de eliminar los extremos flexivos y devolver la forma base o de diccionario de una palabra, conocida como lema.stopwords.words('english')
permítanos ver la lista de todas las palabras vacías en inglés admitidas por NLTK.remove_unused_c()
La función se utiliza para eliminar las columnas no utilizadas.None
el uso de la null_process()
función.clean_dataset()
, llamamos remove_unused_c()
y null_process()
funciones. Esta función es responsable de la limpieza de datos.clean_text()
función.nltk_preprocess()
función para ese propósito.Preprocesando el text
y title
:
# Perform data cleaning on train and test dataset by calling clean_dataset function
df = clean_dataset(news_d)
# apply preprocessing on text through apply method by calling the function nltk_preprocess
df["text"] = df.text.apply(nltk_preprocess)
# apply preprocessing on title through apply method by calling the function nltk_preprocess
df["title"] = df.title.apply(nltk_preprocess)
# Dataset after cleaning and preprocessing step
df.head()
Producción:
title text label
0 house dem aide didnt even see comeys letter ja... house dem aide didnt even see comeys letter ja... 1
1 flynn hillary clinton big woman campus breitbart ever get feeling life circle roundabout rather... 0
2 truth might get fired truth might get fired october 29 2016 tension ... 1
3 15 civilian killed single u airstrike identified video 15 civilian killed single u airstrike id... 1
4 iranian woman jailed fictional unpublished sto... print iranian woman sentenced six year prison ... 1
En esta sección realizaremos:
Las palabras más frecuentes aparecen en negrita y de mayor tamaño en una nube de palabras. Esta sección creará una nube de palabras para todas las palabras del conjunto de datos.
Se usará la función de la biblioteca de WordCloudwordcloud()
y generate()
se utilizará para generar la imagen de la nube de palabras:
from wordcloud import WordCloud, STOPWORDS
import matplotlib.pyplot as plt
# initialize the word cloud
wordcloud = WordCloud( background_color='black', width=800, height=600)
# generate the word cloud by passing the corpus
text_cloud = wordcloud.generate(' '.join(df['text']))
# plotting the word cloud
plt.figure(figsize=(20,30))
plt.imshow(text_cloud)
plt.axis('off')
plt.show()
Producción:
Nube de palabras solo para noticias confiables:
true_n = ' '.join(df[df['label']==0]['text'])
wc = wordcloud.generate(true_n)
plt.figure(figsize=(20,30))
plt.imshow(wc)
plt.axis('off')
plt.show()
Producción:
Nube de palabras solo para noticias falsas:
fake_n = ' '.join(df[df['label']==1]['text'])
wc= wordcloud.generate(fake_n)
plt.figure(figsize=(20,30))
plt.imshow(wc)
plt.axis('off')
plt.show()
Producción:
Un N-grama es una secuencia de letras o palabras. Un unigrama de carácter se compone de un solo carácter, mientras que un bigrama comprende una serie de dos caracteres. De manera similar, los N-gramas de palabras se componen de una serie de n palabras. La palabra "unidos" es un 1 gramo (unigrama). La combinación de las palabras "estado unido" es de 2 gramos (bigrama), "ciudad de nueva york" es de 3 gramos.
Grafiquemos el bigrama más común en las noticias confiables:
def plot_top_ngrams(corpus, title, ylabel, xlabel="Number of Occurences", n=2):
"""Utility function to plot top n-grams"""
true_b = (pd.Series(nltk.ngrams(corpus.split(), n)).value_counts())[:20]
true_b.sort_values().plot.barh(color='blue', width=.9, figsize=(12, 8))
plt.title(title)
plt.ylabel(ylabel)
plt.xlabel(xlabel)
plt.show()
plot_top_ngrams(true_n, 'Top 20 Frequently Occuring True news Bigrams', "Bigram", n=2)
El bigrama más común en las noticias falsas:
plot_top_ngrams(fake_n, 'Top 20 Frequently Occuring Fake news Bigrams', "Bigram", n=2)
El trigrama más común en noticias confiables:
plot_top_ngrams(true_n, 'Top 20 Frequently Occuring True news Trigrams', "Trigrams", n=3)
Para noticias falsas ahora:
plot_top_ngrams(fake_n, 'Top 20 Frequently Occuring Fake news Trigrams', "Trigrams", n=3)
Los gráficos anteriores nos dan algunas ideas sobre cómo se ven ambas clases. En la siguiente sección, usaremos la biblioteca de transformadores para construir un detector de noticias falsas.
Esta sección tomará código ampliamente del tutorial BERT de ajuste fino para hacer un clasificador de noticias falsas utilizando la biblioteca de transformadores. Entonces, para obtener información más detallada, puede dirigirse al tutorial original .
Si no instaló transformadores, debe:
$ pip install transformers
Importemos las bibliotecas necesarias:
import torch
from transformers.file_utils import is_tf_available, is_torch_available, is_torch_tpu_available
from transformers import BertTokenizerFast, BertForSequenceClassification
from transformers import Trainer, TrainingArguments
import numpy as np
from sklearn.model_selection import train_test_split
import random
Queremos que nuestros resultados sean reproducibles incluso si reiniciamos nuestro entorno:
def set_seed(seed: int):
"""
Helper function for reproducible behavior to set the seed in ``random``, ``numpy``, ``torch`` and/or ``tf`` (if
installed).
Args:
seed (:obj:`int`): The seed to set.
"""
random.seed(seed)
np.random.seed(seed)
if is_torch_available():
torch.manual_seed(seed)
torch.cuda.manual_seed_all(seed)
# ^^ safe to call this function even if cuda is not available
if is_tf_available():
import tensorflow as tf
tf.random.set_seed(seed)
set_seed(1)
El modelo que vamos a utilizar es el bert-base-uncased
:
# the model we gonna train, base uncased BERT
# check text classification models here: https://huggingface.co/models?filter=text-classification
model_name = "bert-base-uncased"
# max sequence length for each document/sentence sample
max_length = 512
Cargando el tokenizador:
# load the tokenizer
tokenizer = BertTokenizerFast.from_pretrained(model_name, do_lower_case=True)
Limpiemos ahora los NaN
valores de las columnas text
, author
y :title
news_df = news_d[news_d['text'].notna()]
news_df = news_df[news_df["author"].notna()]
news_df = news_df[news_df["title"].notna()]
A continuación, crear una función que tome el conjunto de datos como un marco de datos de Pandas y devuelva las divisiones de entrenamiento/validación de textos y etiquetas como listas:
def prepare_data(df, test_size=0.2, include_title=True, include_author=True):
texts = []
labels = []
for i in range(len(df)):
text = df["text"].iloc[i]
label = df["label"].iloc[i]
if include_title:
text = df["title"].iloc[i] + " - " + text
if include_author:
text = df["author"].iloc[i] + " : " + text
if text and label in [0, 1]:
texts.append(text)
labels.append(label)
return train_test_split(texts, labels, test_size=test_size)
train_texts, valid_texts, train_labels, valid_labels = prepare_data(news_df)
La función anterior toma el conjunto de datos en un tipo de marco de datos y los devuelve como listas divididas en conjuntos de entrenamiento y validación. Establecer include_title
en True
significa que agregamos la title
columna a la text
que vamos a usar para el entrenamiento, establecer include_author
en True
significa que también agregamos author
al texto.
Asegurémonos de que las etiquetas y los textos tengan la misma longitud:
print(len(train_texts), len(train_labels))
print(len(valid_texts), len(valid_labels))
Producción:
14628 14628
3657 3657
Usemos el tokenizador BERT para tokenizar nuestro conjunto de datos:
# tokenize the dataset, truncate when passed `max_length`,
# and pad with 0's when less than `max_length`
train_encodings = tokenizer(train_texts, truncation=True, padding=True, max_length=max_length)
valid_encodings = tokenizer(valid_texts, truncation=True, padding=True, max_length=max_length)
Convertir las codificaciones en un conjunto de datos de PyTorch:
class NewsGroupsDataset(torch.utils.data.Dataset):
def __init__(self, encodings, labels):
self.encodings = encodings
self.labels = labels
def __getitem__(self, idx):
item = {k: torch.tensor(v[idx]) for k, v in self.encodings.items()}
item["labels"] = torch.tensor([self.labels[idx]])
return item
def __len__(self):
return len(self.labels)
# convert our tokenized data into a torch Dataset
train_dataset = NewsGroupsDataset(train_encodings, train_labels)
valid_dataset = NewsGroupsDataset(valid_encodings, valid_labels)
Usaremos BertForSequenceClassification
para cargar nuestro modelo de transformador BERT:
# load the model
model = BertForSequenceClassification.from_pretrained(model_name, num_labels=2)
Establecemos num_labels
a 2 ya que es una clasificación binaria. A continuación, la función es una devolución de llamada para calcular la precisión en cada paso de validación:
from sklearn.metrics import accuracy_score
def compute_metrics(pred):
labels = pred.label_ids
preds = pred.predictions.argmax(-1)
# calculate accuracy using sklearn's function
acc = accuracy_score(labels, preds)
return {
'accuracy': acc,
}
Vamos a inicializar los parámetros de entrenamiento:
training_args = TrainingArguments(
output_dir='./results', # output directory
num_train_epochs=1, # total number of training epochs
per_device_train_batch_size=10, # batch size per device during training
per_device_eval_batch_size=20, # batch size for evaluation
warmup_steps=100, # number of warmup steps for learning rate scheduler
logging_dir='./logs', # directory for storing logs
load_best_model_at_end=True, # load the best model when finished training (default metric is loss)
# but you can specify `metric_for_best_model` argument to change to accuracy or other metric
logging_steps=200, # log & save weights each logging_steps
save_steps=200,
evaluation_strategy="steps", # evaluate each `logging_steps`
)
Configuré el valor per_device_train_batch_size
en 10, pero debe configurarlo tan alto como su GPU pueda caber. Establecer el logging_steps
y save_steps
en 200, lo que significa que vamos a realizar una evaluación y guardar los pesos del modelo en cada 200 pasos de entrenamiento.
Puede consultar esta página para obtener información más detallada sobre los parámetros de entrenamiento disponibles.
Instanciamos el entrenador:
trainer = Trainer(
model=model, # the instantiated Transformers model to be trained
args=training_args, # training arguments, defined above
train_dataset=train_dataset, # training dataset
eval_dataset=valid_dataset, # evaluation dataset
compute_metrics=compute_metrics, # the callback that computes metrics of interest
)
Entrenamiento del modelo:
# train the model
trainer.train()
El entrenamiento tarda unas horas en finalizar, dependiendo de su GPU. Si está en la versión gratuita de Colab, debería tomar una hora con NVIDIA Tesla K80. Aquí está la salida:
***** Running training *****
Num examples = 14628
Num Epochs = 1
Instantaneous batch size per device = 10
Total train batch size (w. parallel, distributed & accumulation) = 10
Gradient Accumulation steps = 1
Total optimization steps = 1463
[1463/1463 41:07, Epoch 1/1]
Step Training Loss Validation Loss Accuracy
200 0.250800 0.100533 0.983867
400 0.027600 0.043009 0.993437
600 0.023400 0.017812 0.997539
800 0.014900 0.030269 0.994258
1000 0.022400 0.012961 0.998086
1200 0.009800 0.010561 0.998633
1400 0.007700 0.010300 0.998633
***** Running Evaluation *****
Num examples = 3657
Batch size = 20
Saving model checkpoint to ./results/checkpoint-200
Configuration saved in ./results/checkpoint-200/config.json
Model weights saved in ./results/checkpoint-200/pytorch_model.bin
<SNIPPED>
***** Running Evaluation *****
Num examples = 3657
Batch size = 20
Saving model checkpoint to ./results/checkpoint-1400
Configuration saved in ./results/checkpoint-1400/config.json
Model weights saved in ./results/checkpoint-1400/pytorch_model.bin
Training completed. Do not forget to share your model on huggingface.co/models =)
Loading best model from ./results/checkpoint-1400 (score: 0.010299865156412125).
TrainOutput(global_step=1463, training_loss=0.04888018785440506, metrics={'train_runtime': 2469.1722, 'train_samples_per_second': 5.924, 'train_steps_per_second': 0.593, 'total_flos': 3848788517806080.0, 'train_loss': 0.04888018785440506, 'epoch': 1.0})
Dado que load_best_model_at_end
está configurado en True
, los mejores pesos se cargarán cuando se complete el entrenamiento. Vamos a evaluarlo con nuestro conjunto de validación:
# evaluate the current model after training
trainer.evaluate()
Producción:
***** Running Evaluation *****
Num examples = 3657
Batch size = 20
[183/183 02:11]
{'epoch': 1.0,
'eval_accuracy': 0.998632759092152,
'eval_loss': 0.010299865156412125,
'eval_runtime': 132.0374,
'eval_samples_per_second': 27.697,
'eval_steps_per_second': 1.386}
Guardando el modelo y el tokenizador:
# saving the fine tuned model & tokenizer
model_path = "fake-news-bert-base-uncased"
model.save_pretrained(model_path)
tokenizer.save_pretrained(model_path)
Aparecerá una nueva carpeta que contiene la configuración del modelo y los pesos después de ejecutar la celda anterior. Si desea realizar una predicción, simplemente use el from_pretrained()
método que usamos cuando cargamos el modelo, y ya está listo.
A continuación, hagamos una función que acepte el texto del artículo como argumento y devuelva si es falso o no:
def get_prediction(text, convert_to_label=False):
# prepare our text into tokenized sequence
inputs = tokenizer(text, padding=True, truncation=True, max_length=max_length, return_tensors="pt").to("cuda")
# perform inference to our model
outputs = model(**inputs)
# get output probabilities by doing softmax
probs = outputs[0].softmax(1)
# executing argmax function to get the candidate label
d = {
0: "reliable",
1: "fake"
}
if convert_to_label:
return d[int(probs.argmax())]
else:
return int(probs.argmax())
Tomé un ejemplo de test.csv
que el modelo nunca vio para realizar inferencias, lo verifiqué y es un artículo real de The New York Times:
real_news = """
Tim Tebow Will Attempt Another Comeback, This Time in Baseball - The New York Times",Daniel Victor,"If at first you don’t succeed, try a different sport. Tim Tebow, who was a Heisman quarterback at the University of Florida but was unable to hold an N. F. L. job, is pursuing a career in Major League Baseball. <SNIPPED>
"""
El texto original está en el entorno de Colab si desea copiarlo, ya que es un artículo completo. Vamos a pasarlo al modelo y ver los resultados:
get_prediction(real_news, convert_to_label=True)
Producción:
reliable
En esta sección, predeciremos todos los artículos en el test.csv
para crear un archivo de envío para ver nuestra precisión en la prueba establecida en la competencia Kaggle :
# read the test set
test_df = pd.read_csv("test.csv")
# make a copy of the testing set
new_df = test_df.copy()
# add a new column that contains the author, title and article content
new_df["new_text"] = new_df["author"].astype(str) + " : " + new_df["title"].astype(str) + " - " + new_df["text"].astype(str)
# get the prediction of all the test set
new_df["label"] = new_df["new_text"].apply(get_prediction)
# make the submission file
final_df = new_df[["id", "label"]]
final_df.to_csv("submit_final.csv", index=False)
Después de concatenar el autor, el título y el texto del artículo, pasamos la get_prediction()
función a la nueva columna para llenar la label
columna, luego usamos to_csv()
el método para crear el archivo de envío para Kaggle. Aquí está mi puntaje de presentación:
Obtuvimos una precisión del 99,78 % y del 100 % en las tablas de clasificación privadas y públicas. ¡Eso es genial!
Muy bien, hemos terminado con el tutorial. Puede consultar esta página para ver varios parámetros de entrenamiento que puede modificar.
Si tiene un conjunto de datos de noticias falsas personalizado para ajustarlo, simplemente tiene que pasar una lista de muestras al tokenizador como lo hicimos nosotros, no cambiará ningún otro código después de eso.
Consulta el código completo aquí , o el entorno de Colab aquí .
1646051476
Explorer l'ensemble de données de fausses nouvelles, effectuer une analyse de données telles que des nuages de mots et des ngrams, et affiner le transformateur BERT pour créer un détecteur de fausses nouvelles en Python à l'aide de la bibliothèque de transformateurs.
Les fausses nouvelles sont la diffusion intentionnelle d'allégations fausses ou trompeuses en tant que nouvelles, où les déclarations sont délibérément mensongères.
Les journaux, les tabloïds et les magazines ont été supplantés par les plateformes d'actualités numériques, les blogs, les flux de médias sociaux et une pléthore d'applications d'actualités mobiles. Les organes de presse ont profité de l'utilisation accrue des médias sociaux et des plates-formes mobiles en fournissant aux abonnés des informations de dernière minute.
Les consommateurs ont désormais un accès instantané aux dernières nouvelles. Ces plateformes de médias numériques ont gagné en importance en raison de leur connectivité facile au reste du monde et permettent aux utilisateurs de discuter et de partager des idées et de débattre de sujets tels que la démocratie, l'éducation, la santé, la recherche et l'histoire. Les fausses informations sur les plateformes numériques deviennent de plus en plus populaires et sont utilisées à des fins lucratives, telles que des gains politiques et financiers.
Parce qu'Internet, les médias sociaux et les plateformes numériques sont largement utilisés, n'importe qui peut propager des informations inexactes et biaisées. Il est presque impossible d'empêcher la diffusion de fausses nouvelles. Il y a une énorme augmentation de la diffusion de fausses nouvelles, qui ne se limite pas à un secteur comme la politique, mais comprend le sport, la santé, l'histoire, le divertissement, la science et la recherche.
Il est essentiel de reconnaître et de différencier les informations fausses des informations exactes. Une méthode consiste à demander à un expert de décider et de vérifier chaque élément d'information, mais cela prend du temps et nécessite une expertise qui ne peut être partagée. Deuxièmement, nous pouvons utiliser des outils d'apprentissage automatique et d'intelligence artificielle pour automatiser l'identification des fausses nouvelles.
Les informations d'actualité en ligne incluent diverses données de format non structuré (telles que des documents, des vidéos et de l'audio), mais nous nous concentrerons ici sur les informations au format texte. Avec les progrès de l'apprentissage automatique et du traitement automatique du langage naturel , nous pouvons désormais reconnaître le caractère trompeur et faux d'un article ou d'une déclaration.
Plusieurs études et expérimentations sont menées pour détecter les fake news sur tous les supports.
Notre objectif principal de ce tutoriel est :
Voici la table des matières :
Dans ce travail, nous avons utilisé l'ensemble de données sur les fausses nouvelles de Kaggle pour classer les articles d'actualité non fiables comme fausses nouvelles. Nous disposons d'un jeu de données d'entraînement complet contenant les caractéristiques suivantes :
id
: identifiant unique pour un article de pressetitle
: titre d'un article de presseauthor
: auteur de l'article de pressetext
: texte de l'article ; pourrait être incompletlabel
: une étiquette qui marque l'article comme potentiellement non fiable, notée 1 (non fiable ou faux) ou 0 (fiable).Il s'agit d'un problème de classification binaire dans lequel nous devons prédire si une nouvelle particulière est fiable ou non.
Si vous avez un compte Kaggle, vous pouvez simplement télécharger l'ensemble de données à partir du site Web et extraire le fichier ZIP.
J'ai également téléchargé l'ensemble de données dans Google Drive, et vous pouvez l'obtenir ici , ou utiliser la gdown
bibliothèque pour le télécharger automatiquement dans les blocs-notes Google Colab ou Jupyter :
$ pip install gdown
# download from Google Drive
$ gdown "https://drive.google.com/uc?id=178f_VkNxccNidap-5-uffXUW475pAuPy&confirm=t"
Downloading...
From: https://drive.google.com/uc?id=178f_VkNxccNidap-5-uffXUW475pAuPy&confirm=t
To: /content/fake-news.zip
100% 48.7M/48.7M [00:00<00:00, 74.6MB/s]
Décompressez les fichiers :
$ unzip fake-news.zip
Trois fichiers apparaîtront dans le répertoire de travail actuel : train.csv
, test.csv
, et submit.csv
, que nous utiliserons train.csv
dans la majeure partie du didacticiel.
Installation des dépendances requises :
$ pip install transformers nltk pandas numpy matplotlib seaborn wordcloud
Remarque : Si vous êtes dans un environnement local, assurez-vous d'installer PyTorch pour GPU, rendez-vous sur cette page pour une installation correcte.
Importons les bibliothèques essentielles pour l'analyse :
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
Les corpus et modules NLTK doivent être installés à l'aide du téléchargeur NLTK standard :
import nltk
nltk.download('stopwords')
nltk.download('wordnet')
L'ensemble de données sur les fausses nouvelles comprend les titres et le texte d'articles originaux et fictifs de divers auteurs. Importons notre jeu de données :
# load the dataset
news_d = pd.read_csv("train.csv")
print("Shape of News data:", news_d.shape)
print("News data columns", news_d.columns)
Sortir:
Shape of News data: (20800, 5)
News data columns Index(['id', 'title', 'author', 'text', 'label'], dtype='object')
Voici à quoi ressemble l'ensemble de données :
# by using df.head(), we can immediately familiarize ourselves with the dataset.
news_d.head()
Sortir:
id title author text label
0 0 House Dem Aide: We Didn’t Even See Comey’s Let... Darrell Lucus House Dem Aide: We Didn’t Even See Comey’s Let... 1
1 1 FLYNN: Hillary Clinton, Big Woman on Campus - ... Daniel J. Flynn Ever get the feeling your life circles the rou... 0
2 2 Why the Truth Might Get You Fired Consortiumnews.com Why the Truth Might Get You Fired October 29, ... 1
3 3 15 Civilians Killed In Single US Airstrike Hav... Jessica Purkiss Videos 15 Civilians Killed In Single US Airstr... 1
4 4 Iranian woman jailed for fictional unpublished... Howard Portnoy Print \nAn Iranian woman has been sentenced to... 1
Nous avons 20 800 lignes, qui ont cinq colonnes. Voyons quelques statistiques de la text
colonne :
#Text Word startistics: min.mean, max and interquartile range
txt_length = news_d.text.str.split().str.len()
txt_length.describe()
Sortir:
count 20761.000000
mean 760.308126
std 869.525988
min 0.000000
25% 269.000000
50% 556.000000
75% 1052.000000
max 24234.000000
Name: text, dtype: float64
Statistiques pour la title
colonne :
#Title statistics
title_length = news_d.title.str.split().str.len()
title_length.describe()
Sortir:
count 20242.000000
mean 12.420709
std 4.098735
min 1.000000
25% 10.000000
50% 13.000000
75% 15.000000
max 72.000000
Name: title, dtype: float64
Les statistiques pour les ensembles d'entraînement et de test sont les suivantes :
text
attribut a un nombre de mots plus élevé avec une moyenne de 760 mots et 75% ayant plus de 1000 mots.title
attribut est une courte déclaration avec une moyenne de 12 mots, et 75% d'entre eux sont d'environ 15 mots.Notre expérience porterait à la fois sur le texte et le titre.
Compter les parcelles pour les deux étiquettes :
sns.countplot(x="label", data=news_d);
print("1: Unreliable")
print("0: Reliable")
print("Distribution of labels:")
print(news_d.label.value_counts());
Sortir:
1: Unreliable
0: Reliable
Distribution of labels:
1 10413
0 10387
Name: label, dtype: int64
print(round(news_d.label.value_counts(normalize=True),2)*100);
Sortir:
1 50.0
0 50.0
Name: label, dtype: float64
Le nombre d'articles non fiables (faux ou 1) est de 10413, tandis que le nombre d'articles dignes de confiance (fiables ou 0) est de 10387. Près de 50% des articles sont faux. Par conséquent, la métrique de précision mesurera la performance de notre modèle lors de la construction d'un classificateur.
Dans cette section, nous allons nettoyer notre ensemble de données pour effectuer une analyse :
# Constants that are used to sanitize the datasets
column_n = ['id', 'title', 'author', 'text', 'label']
remove_c = ['id','author']
categorical_features = []
target_col = ['label']
text_f = ['title', 'text']
# Clean Datasets
import nltk
from nltk.corpus import stopwords
import re
from nltk.stem.porter import PorterStemmer
from collections import Counter
ps = PorterStemmer()
wnl = nltk.stem.WordNetLemmatizer()
stop_words = stopwords.words('english')
stopwords_dict = Counter(stop_words)
# Removed unused clumns
def remove_unused_c(df,column_n=remove_c):
df = df.drop(column_n,axis=1)
return df
# Impute null values with None
def null_process(feature_df):
for col in text_f:
feature_df.loc[feature_df[col].isnull(), col] = "None"
return feature_df
def clean_dataset(df):
# remove unused column
df = remove_unused_c(df)
#impute null values
df = null_process(df)
return df
# Cleaning text from unused characters
def clean_text(text):
text = str(text).replace(r'http[\w:/\.]+', ' ') # removing urls
text = str(text).replace(r'[^\.\w\s]', ' ') # remove everything but characters and punctuation
text = str(text).replace('[^a-zA-Z]', ' ')
text = str(text).replace(r'\s\s+', ' ')
text = text.lower().strip()
#text = ' '.join(text)
return text
## Nltk Preprocessing include:
# Stop words, Stemming and Lemmetization
# For our project we use only Stop word removal
def nltk_preprocess(text):
text = clean_text(text)
wordlist = re.sub(r'[^\w\s]', '', text).split()
#text = ' '.join([word for word in wordlist if word not in stopwords_dict])
#text = [ps.stem(word) for word in wordlist if not word in stopwords_dict]
text = ' '.join([wnl.lemmatize(word) for word in wordlist if word not in stopwords_dict])
return text
Dans le bloc de code ci-dessus :
re
pour regex.nltk.corpus
. Lorsque nous travaillons avec des mots, en particulier lorsque nous considérons la sémantique, nous devons parfois éliminer les mots courants qui n'ajoutent aucune signification significative à une déclaration, tels que "but"
, "can"
, "we"
, etc.PorterStemmer
est utilisé pour effectuer des mots radicaux avec NLTK. Les radicaux dépouillent les mots de leurs affixes morphologiques, laissant uniquement le radical du mot.WordNetLemmatizer()
de la bibliothèque NLTK pour la lemmatisation. La lemmatisation est bien plus efficace que la radicalisation . Il va au-delà de la réduction des mots et évalue l'ensemble du lexique d'une langue pour appliquer une analyse morphologique aux mots, dans le but de supprimer simplement les extrémités flexionnelles et de renvoyer la forme de base ou de dictionnaire d'un mot, connue sous le nom de lemme.stopwords.words('english')
permettez-nous de regarder la liste de tous les mots vides en anglais pris en charge par NLTK.remove_unused_c()
La fonction est utilisée pour supprimer les colonnes inutilisées.None
l'aide de la null_process()
fonction.clean_dataset()
, nous appelons remove_unused_c()
et null_process()
fonctions. Cette fonction est responsable du nettoyage des données.clean_text()
fonction.nltk_preprocess()
fonction à cet effet.Prétraitement de text
et title
:
# Perform data cleaning on train and test dataset by calling clean_dataset function
df = clean_dataset(news_d)
# apply preprocessing on text through apply method by calling the function nltk_preprocess
df["text"] = df.text.apply(nltk_preprocess)
# apply preprocessing on title through apply method by calling the function nltk_preprocess
df["title"] = df.title.apply(nltk_preprocess)
# Dataset after cleaning and preprocessing step
df.head()
Sortir:
title text label
0 house dem aide didnt even see comeys letter ja... house dem aide didnt even see comeys letter ja... 1
1 flynn hillary clinton big woman campus breitbart ever get feeling life circle roundabout rather... 0
2 truth might get fired truth might get fired october 29 2016 tension ... 1
3 15 civilian killed single u airstrike identified video 15 civilian killed single u airstrike id... 1
4 iranian woman jailed fictional unpublished sto... print iranian woman sentenced six year prison ... 1
Dans cette section, nous effectuerons :
Les mots les plus fréquents apparaissent en caractères gras et plus gros dans un nuage de mots. Cette section effectuera un nuage de mots pour tous les mots du jeu de données.
La fonction de la bibliothèque WordCloudwordcloud()
sera utilisée, et la generate()
est utilisée pour générer l'image du nuage de mots :
from wordcloud import WordCloud, STOPWORDS
import matplotlib.pyplot as plt
# initialize the word cloud
wordcloud = WordCloud( background_color='black', width=800, height=600)
# generate the word cloud by passing the corpus
text_cloud = wordcloud.generate(' '.join(df['text']))
# plotting the word cloud
plt.figure(figsize=(20,30))
plt.imshow(text_cloud)
plt.axis('off')
plt.show()
Sortir:
Nuage de mots pour les informations fiables uniquement :
true_n = ' '.join(df[df['label']==0]['text'])
wc = wordcloud.generate(true_n)
plt.figure(figsize=(20,30))
plt.imshow(wc)
plt.axis('off')
plt.show()
Sortir:
Nuage de mots pour les fake news uniquement :
fake_n = ' '.join(df[df['label']==1]['text'])
wc= wordcloud.generate(fake_n)
plt.figure(figsize=(20,30))
plt.imshow(wc)
plt.axis('off')
plt.show()
Sortir:
Un N-gramme est une séquence de lettres ou de mots. Un unigramme de caractère est composé d'un seul caractère, tandis qu'un bigramme est composé d'une série de deux caractères. De même, les N-grammes de mots sont constitués d'une suite de n mots. Le mot "uni" est un 1-gramme (unigramme). La combinaison des mots "États-Unis" est un 2-gramme (bigramme), "new york city" est un 3-gramme.
Traçons le bigramme le plus courant sur les nouvelles fiables :
def plot_top_ngrams(corpus, title, ylabel, xlabel="Number of Occurences", n=2):
"""Utility function to plot top n-grams"""
true_b = (pd.Series(nltk.ngrams(corpus.split(), n)).value_counts())[:20]
true_b.sort_values().plot.barh(color='blue', width=.9, figsize=(12, 8))
plt.title(title)
plt.ylabel(ylabel)
plt.xlabel(xlabel)
plt.show()
plot_top_ngrams(true_n, 'Top 20 Frequently Occuring True news Bigrams', "Bigram", n=2)
Le bigramme le plus courant sur les fake news :
plot_top_ngrams(fake_n, 'Top 20 Frequently Occuring Fake news Bigrams', "Bigram", n=2)
Le trigramme le plus courant sur les informations fiables :
plot_top_ngrams(true_n, 'Top 20 Frequently Occuring True news Trigrams', "Trigrams", n=3)
Pour les fausses nouvelles maintenant :
plot_top_ngrams(fake_n, 'Top 20 Frequently Occuring Fake news Trigrams', "Trigrams", n=3)
Les tracés ci-dessus nous donnent quelques idées sur l'apparence des deux classes. Dans la section suivante, nous utiliserons la bibliothèque de transformateurs pour créer un détecteur de fausses nouvelles.
Cette section récupèrera largement le code du tutoriel de réglage fin du BERT pour créer un classificateur de fausses nouvelles à l'aide de la bibliothèque de transformateurs. Ainsi, pour des informations plus détaillées, vous pouvez vous diriger vers le tutoriel d'origine .
Si vous n'avez pas installé de transformateurs, vous devez :
$ pip install transformers
Importons les bibliothèques nécessaires :
import torch
from transformers.file_utils import is_tf_available, is_torch_available, is_torch_tpu_available
from transformers import BertTokenizerFast, BertForSequenceClassification
from transformers import Trainer, TrainingArguments
import numpy as np
from sklearn.model_selection import train_test_split
import random
Nous voulons rendre nos résultats reproductibles même si nous redémarrons notre environnement :
def set_seed(seed: int):
"""
Helper function for reproducible behavior to set the seed in ``random``, ``numpy``, ``torch`` and/or ``tf`` (if
installed).
Args:
seed (:obj:`int`): The seed to set.
"""
random.seed(seed)
np.random.seed(seed)
if is_torch_available():
torch.manual_seed(seed)
torch.cuda.manual_seed_all(seed)
# ^^ safe to call this function even if cuda is not available
if is_tf_available():
import tensorflow as tf
tf.random.set_seed(seed)
set_seed(1)
Le modèle que nous allons utiliser est le bert-base-uncased
:
# the model we gonna train, base uncased BERT
# check text classification models here: https://huggingface.co/models?filter=text-classification
model_name = "bert-base-uncased"
# max sequence length for each document/sentence sample
max_length = 512
Chargement du tokenizer :
# load the tokenizer
tokenizer = BertTokenizerFast.from_pretrained(model_name, do_lower_case=True)
Nettoyons maintenant les NaN
valeurs des colonnes text
, author
et :title
news_df = news_d[news_d['text'].notna()]
news_df = news_df[news_df["author"].notna()]
news_df = news_df[news_df["title"].notna()]
Ensuite, créez une fonction qui prend l'ensemble de données en tant que dataframe Pandas et renvoie les fractionnements de train/validation des textes et des étiquettes sous forme de listes :
def prepare_data(df, test_size=0.2, include_title=True, include_author=True):
texts = []
labels = []
for i in range(len(df)):
text = df["text"].iloc[i]
label = df["label"].iloc[i]
if include_title:
text = df["title"].iloc[i] + " - " + text
if include_author:
text = df["author"].iloc[i] + " : " + text
if text and label in [0, 1]:
texts.append(text)
labels.append(label)
return train_test_split(texts, labels, test_size=test_size)
train_texts, valid_texts, train_labels, valid_labels = prepare_data(news_df)
La fonction ci-dessus prend l'ensemble de données dans un type de trame de données et les renvoie sous forme de listes divisées en ensembles d'apprentissage et de validation. Définir include_title
sur True
signifie que nous ajoutons la title
colonne à celle text
que nous allons utiliser pour la formation, définir include_author
sur True
signifie que nous ajoutons author
également la au texte.
Assurons-nous que les étiquettes et les textes ont la même longueur :
print(len(train_texts), len(train_labels))
print(len(valid_texts), len(valid_labels))
Sortir:
14628 14628
3657 3657
Utilisons le tokenizer BERT pour tokeniser notre jeu de données :
# tokenize the dataset, truncate when passed `max_length`,
# and pad with 0's when less than `max_length`
train_encodings = tokenizer(train_texts, truncation=True, padding=True, max_length=max_length)
valid_encodings = tokenizer(valid_texts, truncation=True, padding=True, max_length=max_length)
Conversion des encodages en un jeu de données PyTorch :
class NewsGroupsDataset(torch.utils.data.Dataset):
def __init__(self, encodings, labels):
self.encodings = encodings
self.labels = labels
def __getitem__(self, idx):
item = {k: torch.tensor(v[idx]) for k, v in self.encodings.items()}
item["labels"] = torch.tensor([self.labels[idx]])
return item
def __len__(self):
return len(self.labels)
# convert our tokenized data into a torch Dataset
train_dataset = NewsGroupsDataset(train_encodings, train_labels)
valid_dataset = NewsGroupsDataset(valid_encodings, valid_labels)
Nous utiliserons BertForSequenceClassification
pour charger notre modèle de transformateur BERT :
# load the model
model = BertForSequenceClassification.from_pretrained(model_name, num_labels=2)
Nous avons mis num_labels
à 2 puisqu'il s'agit d'une classification binaire. La fonction ci-dessous est un rappel pour calculer la précision à chaque étape de validation :
from sklearn.metrics import accuracy_score
def compute_metrics(pred):
labels = pred.label_ids
preds = pred.predictions.argmax(-1)
# calculate accuracy using sklearn's function
acc = accuracy_score(labels, preds)
return {
'accuracy': acc,
}
Initialisons les paramètres d'entraînement :
training_args = TrainingArguments(
output_dir='./results', # output directory
num_train_epochs=1, # total number of training epochs
per_device_train_batch_size=10, # batch size per device during training
per_device_eval_batch_size=20, # batch size for evaluation
warmup_steps=100, # number of warmup steps for learning rate scheduler
logging_dir='./logs', # directory for storing logs
load_best_model_at_end=True, # load the best model when finished training (default metric is loss)
# but you can specify `metric_for_best_model` argument to change to accuracy or other metric
logging_steps=200, # log & save weights each logging_steps
save_steps=200,
evaluation_strategy="steps", # evaluate each `logging_steps`
)
J'ai réglé le per_device_train_batch_size
à 10, mais vous devriez le régler aussi haut que votre GPU pourrait éventuellement s'adapter. En réglant le logging_steps
et save_steps
sur 200, cela signifie que nous allons effectuer une évaluation et enregistrer les poids du modèle à chaque étape de formation de 200.
Vous pouvez consulter cette page pour des informations plus détaillées sur les paramètres d'entraînement disponibles.
Instancions le formateur :
trainer = Trainer(
model=model, # the instantiated Transformers model to be trained
args=training_args, # training arguments, defined above
train_dataset=train_dataset, # training dataset
eval_dataset=valid_dataset, # evaluation dataset
compute_metrics=compute_metrics, # the callback that computes metrics of interest
)
Entraînement du modèle :
# train the model
trainer.train()
La formation prend quelques heures pour se terminer, en fonction de votre GPU. Si vous êtes sur la version gratuite de Colab, cela devrait prendre une heure avec NVIDIA Tesla K80. Voici la sortie :
***** Running training *****
Num examples = 14628
Num Epochs = 1
Instantaneous batch size per device = 10
Total train batch size (w. parallel, distributed & accumulation) = 10
Gradient Accumulation steps = 1
Total optimization steps = 1463
[1463/1463 41:07, Epoch 1/1]
Step Training Loss Validation Loss Accuracy
200 0.250800 0.100533 0.983867
400 0.027600 0.043009 0.993437
600 0.023400 0.017812 0.997539
800 0.014900 0.030269 0.994258
1000 0.022400 0.012961 0.998086
1200 0.009800 0.010561 0.998633
1400 0.007700 0.010300 0.998633
***** Running Evaluation *****
Num examples = 3657
Batch size = 20
Saving model checkpoint to ./results/checkpoint-200
Configuration saved in ./results/checkpoint-200/config.json
Model weights saved in ./results/checkpoint-200/pytorch_model.bin
<SNIPPED>
***** Running Evaluation *****
Num examples = 3657
Batch size = 20
Saving model checkpoint to ./results/checkpoint-1400
Configuration saved in ./results/checkpoint-1400/config.json
Model weights saved in ./results/checkpoint-1400/pytorch_model.bin
Training completed. Do not forget to share your model on huggingface.co/models =)
Loading best model from ./results/checkpoint-1400 (score: 0.010299865156412125).
TrainOutput(global_step=1463, training_loss=0.04888018785440506, metrics={'train_runtime': 2469.1722, 'train_samples_per_second': 5.924, 'train_steps_per_second': 0.593, 'total_flos': 3848788517806080.0, 'train_loss': 0.04888018785440506, 'epoch': 1.0})
Étant donné que load_best_model_at_end
est réglé sur True
, les meilleurs poids seront chargés une fois l'entraînement terminé. Évaluons-le avec notre ensemble de validation :
# evaluate the current model after training
trainer.evaluate()
Sortir:
***** Running Evaluation *****
Num examples = 3657
Batch size = 20
[183/183 02:11]
{'epoch': 1.0,
'eval_accuracy': 0.998632759092152,
'eval_loss': 0.010299865156412125,
'eval_runtime': 132.0374,
'eval_samples_per_second': 27.697,
'eval_steps_per_second': 1.386}
Enregistrement du modèle et du tokenizer :
# saving the fine tuned model & tokenizer
model_path = "fake-news-bert-base-uncased"
model.save_pretrained(model_path)
tokenizer.save_pretrained(model_path)
Un nouveau dossier contenant la configuration du modèle et les poids apparaîtra après l'exécution de la cellule ci-dessus. Si vous souhaitez effectuer une prédiction, vous utilisez simplement la from_pretrained()
méthode que nous avons utilisée lorsque nous avons chargé le modèle, et vous êtes prêt à partir.
Ensuite, créons une fonction qui accepte le texte de l'article comme argument et retourne s'il est faux ou non :
def get_prediction(text, convert_to_label=False):
# prepare our text into tokenized sequence
inputs = tokenizer(text, padding=True, truncation=True, max_length=max_length, return_tensors="pt").to("cuda")
# perform inference to our model
outputs = model(**inputs)
# get output probabilities by doing softmax
probs = outputs[0].softmax(1)
# executing argmax function to get the candidate label
d = {
0: "reliable",
1: "fake"
}
if convert_to_label:
return d[int(probs.argmax())]
else:
return int(probs.argmax())
J'ai pris un exemple à partir test.csv
duquel le modèle n'a jamais vu effectuer d'inférence, je l'ai vérifié, et c'est un article réel du New York Times :
real_news = """
Tim Tebow Will Attempt Another Comeback, This Time in Baseball - The New York Times",Daniel Victor,"If at first you don’t succeed, try a different sport. Tim Tebow, who was a Heisman quarterback at the University of Florida but was unable to hold an N. F. L. job, is pursuing a career in Major League Baseball. <SNIPPED>
"""
Le texte original se trouve dans l'environnement Colab si vous souhaitez le copier, car il s'agit d'un article complet. Passons-le au modèle et voyons les résultats :
get_prediction(real_news, convert_to_label=True)
Sortir:
reliable
Dans cette section, nous allons prédire tous les articles dans le test.csv
pour créer un dossier de soumission pour voir notre justesse dans le jeu de test sur le concours Kaggle :
# read the test set
test_df = pd.read_csv("test.csv")
# make a copy of the testing set
new_df = test_df.copy()
# add a new column that contains the author, title and article content
new_df["new_text"] = new_df["author"].astype(str) + " : " + new_df["title"].astype(str) + " - " + new_df["text"].astype(str)
# get the prediction of all the test set
new_df["label"] = new_df["new_text"].apply(get_prediction)
# make the submission file
final_df = new_df[["id", "label"]]
final_df.to_csv("submit_final.csv", index=False)
Après avoir concaténé l'auteur, le titre et le texte de l'article, nous passons la get_prediction()
fonction à la nouvelle colonne pour remplir la label
colonne, nous utilisons ensuite la to_csv()
méthode pour créer le fichier de soumission pour Kaggle. Voici mon score de soumission :
Nous avons obtenu une précision de 99,78 % et 100 % sur les classements privés et publics. C'est génial!
Très bien, nous avons terminé avec le tutoriel. Vous pouvez consulter cette page pour voir divers paramètres d'entraînement que vous pouvez modifier.
Si vous avez un ensemble de données de fausses nouvelles personnalisé pour un réglage fin, il vous suffit de transmettre une liste d'échantillons au tokenizer comme nous l'avons fait, vous ne modifierez plus aucun autre code par la suite.
Vérifiez le code complet ici , ou l'environnement Colab ici .
1646040540
Изучение набора данных фейковых новостей, выполнение анализа данных, таких как облака слов и энграммы, а также тонкая настройка преобразователя BERT для создания детектора фейковых новостей в Python с использованием библиотеки трансформаторов.
Фейковые новости — это преднамеренная трансляция ложных или вводящих в заблуждение заявлений в качестве новостей, где заявления намеренно лживы.
Газеты, таблоиды и журналы были вытеснены цифровыми новостными платформами, блогами, лентами социальных сетей и множеством мобильных новостных приложений. Новостные организации выиграли от более широкого использования социальных сетей и мобильных платформ, предоставляя подписчикам самую свежую информацию.
Потребители теперь имеют мгновенный доступ к последним новостям. Эти цифровые медиа-платформы приобрели известность благодаря своей легкой связи с остальным миром и позволяют пользователям обсуждать и делиться идеями и обсуждать такие темы, как демократия, образование, здравоохранение, исследования и история. Поддельные новости на цифровых платформах становятся все более популярными и используются для получения прибыли, например политической и финансовой выгоды.
Поскольку Интернет, социальные сети и цифровые платформы широко используются, любой может распространять неточную и предвзятую информацию. Предотвратить распространение фейковых новостей практически невозможно. Наблюдается огромный всплеск распространения ложных новостей, который не ограничивается одним сектором, таким как политика, но включает спорт, здравоохранение, историю, развлечения, науку и исследования.
Очень важно распознавать и различать ложные и точные новости. Один из методов заключается в том, чтобы эксперт принимал решение и проверял каждую часть информации, но это требует времени и опыта, которым нельзя поделиться. Во-вторых, мы можем использовать машинное обучение и инструменты искусственного интеллекта для автоматизации выявления фейковых новостей.
Новостная онлайн-информация включает в себя различные данные в неструктурированном формате (такие как документы, видео и аудио), но здесь мы сосредоточимся на новостях в текстовом формате. С развитием машинного обучения и обработки естественного языка мы теперь можем распознавать вводящий в заблуждение и ложный характер статьи или заявления.
Проводится несколько исследований и экспериментов для обнаружения фейковых новостей во всех средах.
Наша основная цель этого урока:
Вот оглавление:
В этой работе мы использовали набор данных о фальшивых новостях от Kaggle , чтобы классифицировать ненадежные новостные статьи как фальшивые новости. У нас есть полный набор обучающих данных, содержащий следующие характеристики:
id
: уникальный идентификатор новостной статьиtitle
: название новостной статьиauthor
: автор новостной статьиtext
: текст статьи; может быть неполнымlabel
: метка, помечающая статью как потенциально ненадежную и обозначаемая цифрой 1 (ненадежная или поддельная) или 0 (надежная).Это проблема бинарной классификации, в которой мы должны предсказать, является ли конкретная новость достоверной или нет.
Если у вас есть учетная запись Kaggle, вы можете просто загрузить набор данных с веб-сайта и извлечь ZIP-файл.
Я также загрузил набор данных в Google Drive, и вы можете получить его здесь , или использовать gdown
библиотеку для автоматической загрузки в блокноты Google Colab или Jupyter:
$ pip install gdown
# download from Google Drive
$ gdown "https://drive.google.com/uc?id=178f_VkNxccNidap-5-uffXUW475pAuPy&confirm=t"
Downloading...
From: https://drive.google.com/uc?id=178f_VkNxccNidap-5-uffXUW475pAuPy&confirm=t
To: /content/fake-news.zip
100% 48.7M/48.7M [00:00<00:00, 74.6MB/s]
Распаковка файлов:
$ unzip fake-news.zip
В текущем рабочем каталоге появятся три файла: train.csv
, test.csv
, и submit.csv
, которые мы будем использовать train.csv
в большей части урока.
Установка необходимых зависимостей:
$ pip install transformers nltk pandas numpy matplotlib seaborn wordcloud
Примечание. Если вы находитесь в локальной среде, убедитесь, что вы установили PyTorch для GPU, перейдите на эту страницу для правильной установки.
Давайте импортируем необходимые библиотеки для анализа:
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
Корпуса и модули NLTK должны быть установлены с помощью стандартного загрузчика NLTK:
import nltk
nltk.download('stopwords')
nltk.download('wordnet')
Набор данных фейковых новостей включает в себя оригинальные и вымышленные заголовки и тексты статей разных авторов. Давайте импортируем наш набор данных:
# load the dataset
news_d = pd.read_csv("train.csv")
print("Shape of News data:", news_d.shape)
print("News data columns", news_d.columns)
Выход:
Shape of News data: (20800, 5)
News data columns Index(['id', 'title', 'author', 'text', 'label'], dtype='object')
Вот как выглядит набор данных:
# by using df.head(), we can immediately familiarize ourselves with the dataset.
news_d.head()
Выход:
id title author text label
0 0 House Dem Aide: We Didn’t Even See Comey’s Let... Darrell Lucus House Dem Aide: We Didn’t Even See Comey’s Let... 1
1 1 FLYNN: Hillary Clinton, Big Woman on Campus - ... Daniel J. Flynn Ever get the feeling your life circles the rou... 0
2 2 Why the Truth Might Get You Fired Consortiumnews.com Why the Truth Might Get You Fired October 29, ... 1
3 3 15 Civilians Killed In Single US Airstrike Hav... Jessica Purkiss Videos 15 Civilians Killed In Single US Airstr... 1
4 4 Iranian woman jailed for fictional unpublished... Howard Portnoy Print \nAn Iranian woman has been sentenced to... 1
У нас есть 20 800 строк с пятью столбцами. Посмотрим немного статистики text
столбца:
#Text Word startistics: min.mean, max and interquartile range
txt_length = news_d.text.str.split().str.len()
txt_length.describe()
Выход:
count 20761.000000
mean 760.308126
std 869.525988
min 0.000000
25% 269.000000
50% 556.000000
75% 1052.000000
max 24234.000000
Name: text, dtype: float64
Статистика по title
колонке:
#Title statistics
title_length = news_d.title.str.split().str.len()
title_length.describe()
Выход:
count 20242.000000
mean 12.420709
std 4.098735
min 1.000000
25% 10.000000
50% 13.000000
75% 15.000000
max 72.000000
Name: title, dtype: float64
Статистика для тренировочного и тестового наборов выглядит следующим образом:
text
имеет более высокое количество слов, в среднем 760 слов, а 75% имеют более 1000 слов.title
представляет собой короткое утверждение, в среднем состоящее из 12 слов, а 75% из них составляют около 15 слов.Наш эксперимент будет с текстом и заголовком вместе.
Графики подсчета для обеих меток:
sns.countplot(x="label", data=news_d);
print("1: Unreliable")
print("0: Reliable")
print("Distribution of labels:")
print(news_d.label.value_counts());
Выход:
1: Unreliable
0: Reliable
Distribution of labels:
1 10413
0 10387
Name: label, dtype: int64
print(round(news_d.label.value_counts(normalize=True),2)*100);
Выход:
1 50.0
0 50.0
Name: label, dtype: float64
Количество ненадежных статей (фейк или 1) — 10413, а количество заслуживающих доверия статей (надежных или 0) — 10387. Почти 50% статей фейковые. Таким образом, метрика точности будет измерять, насколько хорошо работает наша модель при построении классификатора.
В этом разделе мы очистим наш набор данных, чтобы провести некоторый анализ:
# Constants that are used to sanitize the datasets
column_n = ['id', 'title', 'author', 'text', 'label']
remove_c = ['id','author']
categorical_features = []
target_col = ['label']
text_f = ['title', 'text']
# Clean Datasets
import nltk
from nltk.corpus import stopwords
import re
from nltk.stem.porter import PorterStemmer
from collections import Counter
ps = PorterStemmer()
wnl = nltk.stem.WordNetLemmatizer()
stop_words = stopwords.words('english')
stopwords_dict = Counter(stop_words)
# Removed unused clumns
def remove_unused_c(df,column_n=remove_c):
df = df.drop(column_n,axis=1)
return df
# Impute null values with None
def null_process(feature_df):
for col in text_f:
feature_df.loc[feature_df[col].isnull(), col] = "None"
return feature_df
def clean_dataset(df):
# remove unused column
df = remove_unused_c(df)
#impute null values
df = null_process(df)
return df
# Cleaning text from unused characters
def clean_text(text):
text = str(text).replace(r'http[\w:/\.]+', ' ') # removing urls
text = str(text).replace(r'[^\.\w\s]', ' ') # remove everything but characters and punctuation
text = str(text).replace('[^a-zA-Z]', ' ')
text = str(text).replace(r'\s\s+', ' ')
text = text.lower().strip()
#text = ' '.join(text)
return text
## Nltk Preprocessing include:
# Stop words, Stemming and Lemmetization
# For our project we use only Stop word removal
def nltk_preprocess(text):
text = clean_text(text)
wordlist = re.sub(r'[^\w\s]', '', text).split()
#text = ' '.join([word for word in wordlist if word not in stopwords_dict])
#text = [ps.stem(word) for word in wordlist if not word in stopwords_dict]
text = ' '.join([wnl.lemmatize(word) for word in wordlist if word not in stopwords_dict])
return text
В блоке кода выше:
re
для регулярного выражения.nltk.corpus
. При работе со словами, особенно при рассмотрении семантики, нам иногда приходится исключать общеупотребительные слова, которые не добавляют существенного значения высказыванию, например "but"
, "can"
, "we"
, и т. д.PorterStemmer
используется для определения основы слов с помощью NLTK. Стеммеры лишают слова их морфологических аффиксов, оставляя только основу слова.WordNetLemmatizer()
из библиотеки NLTK для лемматизации. Лемматизация намного эффективнее стемминга . Он выходит за рамки сокращения слов и оценивает весь словарный запас языка, чтобы применить морфологический анализ к словам с целью просто удалить флективные окончания и вернуть базовую или словарную форму слова, известную как лемма.stopwords.words('english')
позвольте нам взглянуть на список всех английских стоп-слов, поддерживаемых NLTK.remove_unused_c()
Функция используется для удаления неиспользуемых столбцов.None
помощью null_process()
функции.clean_dataset()
мы вызываем remove_unused_c()
и null_process()
functions. Эта функция отвечает за очистку данных.clean_text()
функцию.nltk_preprocess()
функцию для этой цели.Предварительная обработка text
и title
:
# Perform data cleaning on train and test dataset by calling clean_dataset function
df = clean_dataset(news_d)
# apply preprocessing on text through apply method by calling the function nltk_preprocess
df["text"] = df.text.apply(nltk_preprocess)
# apply preprocessing on title through apply method by calling the function nltk_preprocess
df["title"] = df.title.apply(nltk_preprocess)
# Dataset after cleaning and preprocessing step
df.head()
Выход:
title text label
0 house dem aide didnt even see comeys letter ja... house dem aide didnt even see comeys letter ja... 1
1 flynn hillary clinton big woman campus breitbart ever get feeling life circle roundabout rather... 0
2 truth might get fired truth might get fired october 29 2016 tension ... 1
3 15 civilian killed single u airstrike identified video 15 civilian killed single u airstrike id... 1
4 iranian woman jailed fictional unpublished sto... print iranian woman sentenced six year prison ... 1
В этом разделе мы выполним:
Наиболее часто встречающиеся слова выделены жирным и крупным шрифтом в облаке слов. В этом разделе будет создано облако слов для всех слов в наборе данных.
Будет использоваться функция библиотеки WordCloudwordcloud()
, а для generate()
создания изображения облака слов:
from wordcloud import WordCloud, STOPWORDS
import matplotlib.pyplot as plt
# initialize the word cloud
wordcloud = WordCloud( background_color='black', width=800, height=600)
# generate the word cloud by passing the corpus
text_cloud = wordcloud.generate(' '.join(df['text']))
# plotting the word cloud
plt.figure(figsize=(20,30))
plt.imshow(text_cloud)
plt.axis('off')
plt.show()
Выход:
Облако слов только для достоверных новостей:
true_n = ' '.join(df[df['label']==0]['text'])
wc = wordcloud.generate(true_n)
plt.figure(figsize=(20,30))
plt.imshow(wc)
plt.axis('off')
plt.show()
Выход:
Облако слов только для фейковых новостей:
fake_n = ' '.join(df[df['label']==1]['text'])
wc= wordcloud.generate(fake_n)
plt.figure(figsize=(20,30))
plt.imshow(wc)
plt.axis('off')
plt.show()
Выход:
N-грамма — это последовательность букв или слов. Униграмма символов состоит из одного символа, а биграмма состоит из последовательности из двух символов. Точно так же словесные N-граммы состоят из последовательности n слов. Слово «объединенный» — это 1-грамм (unigram). Сочетание слов «Юнайтед Стейт» — 2-граммовое (биграммное), «Нью-Йорк Сити» — 3-граммовое.
Давайте построим наиболее распространенную биграмму на достоверных новостях:
def plot_top_ngrams(corpus, title, ylabel, xlabel="Number of Occurences", n=2):
"""Utility function to plot top n-grams"""
true_b = (pd.Series(nltk.ngrams(corpus.split(), n)).value_counts())[:20]
true_b.sort_values().plot.barh(color='blue', width=.9, figsize=(12, 8))
plt.title(title)
plt.ylabel(ylabel)
plt.xlabel(xlabel)
plt.show()
plot_top_ngrams(true_n, 'Top 20 Frequently Occuring True news Bigrams', "Bigram", n=2)
Самая распространенная биграмма в фейковых новостях:
plot_top_ngrams(fake_n, 'Top 20 Frequently Occuring Fake news Bigrams', "Bigram", n=2)
Самая распространенная триграмма на достоверных новостях:
plot_top_ngrams(true_n, 'Top 20 Frequently Occuring True news Trigrams', "Trigrams", n=3)
Для фейковых новостей сейчас:
plot_top_ngrams(fake_n, 'Top 20 Frequently Occuring Fake news Trigrams', "Trigrams", n=3)
Приведенные выше графики дают нам некоторое представление о том, как выглядят оба класса. В следующем разделе мы будем использовать библиотеку transforms для создания детектора фейковых новостей.
В этом разделе будет широко использоваться код из руководства по тонкой настройке BERT для создания классификатора поддельных новостей с использованием библиотеки трансформеров. Итак, за более подробной информацией вы можете обратиться к оригинальному туториалу .
Если вы не устанавливали трансформаторы, вам необходимо:
$ pip install transformers
Импортируем необходимые библиотеки:
import torch
from transformers.file_utils import is_tf_available, is_torch_available, is_torch_tpu_available
from transformers import BertTokenizerFast, BertForSequenceClassification
from transformers import Trainer, TrainingArguments
import numpy as np
from sklearn.model_selection import train_test_split
import random
Мы хотим, чтобы наши результаты воспроизводились, даже если мы перезапустим нашу среду:
def set_seed(seed: int):
"""
Helper function for reproducible behavior to set the seed in ``random``, ``numpy``, ``torch`` and/or ``tf`` (if
installed).
Args:
seed (:obj:`int`): The seed to set.
"""
random.seed(seed)
np.random.seed(seed)
if is_torch_available():
torch.manual_seed(seed)
torch.cuda.manual_seed_all(seed)
# ^^ safe to call this function even if cuda is not available
if is_tf_available():
import tensorflow as tf
tf.random.set_seed(seed)
set_seed(1)
Модель, которую мы собираемся использовать, это bert-base-uncased
:
# the model we gonna train, base uncased BERT
# check text classification models here: https://huggingface.co/models?filter=text-classification
model_name = "bert-base-uncased"
# max sequence length for each document/sentence sample
max_length = 512
Загрузка токенизатора:
# load the tokenizer
tokenizer = BertTokenizerFast.from_pretrained(model_name, do_lower_case=True)
Давайте теперь очистим NaN
значения из text
, author
и title
столбцов:
news_df = news_d[news_d['text'].notna()]
news_df = news_df[news_df["author"].notna()]
news_df = news_df[news_df["title"].notna()]
Затем создадим функцию, которая принимает набор данных в качестве фрейма данных Pandas и возвращает разделение текстов и меток для обучения/проверки в виде списков:
def prepare_data(df, test_size=0.2, include_title=True, include_author=True):
texts = []
labels = []
for i in range(len(df)):
text = df["text"].iloc[i]
label = df["label"].iloc[i]
if include_title:
text = df["title"].iloc[i] + " - " + text
if include_author:
text = df["author"].iloc[i] + " : " + text
if text and label in [0, 1]:
texts.append(text)
labels.append(label)
return train_test_split(texts, labels, test_size=test_size)
train_texts, valid_texts, train_labels, valid_labels = prepare_data(news_df)
Приведенная выше функция принимает набор данных в виде фрейма данных и возвращает их в виде списков, разделенных на наборы для обучения и проверки. Значение include_title
означает True
, что мы добавляем title
столбец в столбец, который text
будем использовать для обучения, а значение include_author
означает , True
что мы также добавляем author
его в текст.
Давайте удостоверимся, что метки и тексты имеют одинаковую длину:
print(len(train_texts), len(train_labels))
print(len(valid_texts), len(valid_labels))
Выход:
14628 14628
3657 3657
Давайте используем токенизатор BERT для токенизации нашего набора данных:
# tokenize the dataset, truncate when passed `max_length`,
# and pad with 0's when less than `max_length`
train_encodings = tokenizer(train_texts, truncation=True, padding=True, max_length=max_length)
valid_encodings = tokenizer(valid_texts, truncation=True, padding=True, max_length=max_length)
Преобразование кодировок в набор данных PyTorch:
class NewsGroupsDataset(torch.utils.data.Dataset):
def __init__(self, encodings, labels):
self.encodings = encodings
self.labels = labels
def __getitem__(self, idx):
item = {k: torch.tensor(v[idx]) for k, v in self.encodings.items()}
item["labels"] = torch.tensor([self.labels[idx]])
return item
def __len__(self):
return len(self.labels)
# convert our tokenized data into a torch Dataset
train_dataset = NewsGroupsDataset(train_encodings, train_labels)
valid_dataset = NewsGroupsDataset(valid_encodings, valid_labels)
Мы будем использовать BertForSequenceClassification
для загрузки нашей модели трансформатора BERT:
# load the model
model = BertForSequenceClassification.from_pretrained(model_name, num_labels=2)
Мы установили num_labels
значение 2, так как это бинарная классификация. Ниже функция представляет собой обратный вызов для расчета точности на каждом этапе проверки:
from sklearn.metrics import accuracy_score
def compute_metrics(pred):
labels = pred.label_ids
preds = pred.predictions.argmax(-1)
# calculate accuracy using sklearn's function
acc = accuracy_score(labels, preds)
return {
'accuracy': acc,
}
Давайте инициализируем параметры обучения:
training_args = TrainingArguments(
output_dir='./results', # output directory
num_train_epochs=1, # total number of training epochs
per_device_train_batch_size=10, # batch size per device during training
per_device_eval_batch_size=20, # batch size for evaluation
warmup_steps=100, # number of warmup steps for learning rate scheduler
logging_dir='./logs', # directory for storing logs
load_best_model_at_end=True, # load the best model when finished training (default metric is loss)
# but you can specify `metric_for_best_model` argument to change to accuracy or other metric
logging_steps=200, # log & save weights each logging_steps
save_steps=200,
evaluation_strategy="steps", # evaluate each `logging_steps`
)
Я установил per_device_train_batch_size
значение 10, но вы должны установить его настолько высоко, насколько это возможно для вашего графического процессора. Установите logging_steps
и save_steps
на 200, что означает, что мы собираемся выполнить оценку и сохранить веса модели на каждом шаге обучения 200.
Вы можете проверить эту страницу для получения более подробной информации о доступных параметрах обучения.
Давайте создадим экземпляр тренера:
trainer = Trainer(
model=model, # the instantiated Transformers model to be trained
args=training_args, # training arguments, defined above
train_dataset=train_dataset, # training dataset
eval_dataset=valid_dataset, # evaluation dataset
compute_metrics=compute_metrics, # the callback that computes metrics of interest
)
Обучение модели:
# train the model
trainer.train()
Обучение занимает несколько часов, в зависимости от вашего графического процессора. Если вы используете бесплатную версию Colab, это займет час с NVIDIA Tesla K80. Вот результат:
***** Running training *****
Num examples = 14628
Num Epochs = 1
Instantaneous batch size per device = 10
Total train batch size (w. parallel, distributed & accumulation) = 10
Gradient Accumulation steps = 1
Total optimization steps = 1463
[1463/1463 41:07, Epoch 1/1]
Step Training Loss Validation Loss Accuracy
200 0.250800 0.100533 0.983867
400 0.027600 0.043009 0.993437
600 0.023400 0.017812 0.997539
800 0.014900 0.030269 0.994258
1000 0.022400 0.012961 0.998086
1200 0.009800 0.010561 0.998633
1400 0.007700 0.010300 0.998633
***** Running Evaluation *****
Num examples = 3657
Batch size = 20
Saving model checkpoint to ./results/checkpoint-200
Configuration saved in ./results/checkpoint-200/config.json
Model weights saved in ./results/checkpoint-200/pytorch_model.bin
<SNIPPED>
***** Running Evaluation *****
Num examples = 3657
Batch size = 20
Saving model checkpoint to ./results/checkpoint-1400
Configuration saved in ./results/checkpoint-1400/config.json
Model weights saved in ./results/checkpoint-1400/pytorch_model.bin
Training completed. Do not forget to share your model on huggingface.co/models =)
Loading best model from ./results/checkpoint-1400 (score: 0.010299865156412125).
TrainOutput(global_step=1463, training_loss=0.04888018785440506, metrics={'train_runtime': 2469.1722, 'train_samples_per_second': 5.924, 'train_steps_per_second': 0.593, 'total_flos': 3848788517806080.0, 'train_loss': 0.04888018785440506, 'epoch': 1.0})
Поскольку load_best_model_at_end
установлено значение True
, лучшие веса будут загружены после завершения тренировки. Давайте оценим это с помощью нашего набора проверки:
# evaluate the current model after training
trainer.evaluate()
Выход:
***** Running Evaluation *****
Num examples = 3657
Batch size = 20
[183/183 02:11]
{'epoch': 1.0,
'eval_accuracy': 0.998632759092152,
'eval_loss': 0.010299865156412125,
'eval_runtime': 132.0374,
'eval_samples_per_second': 27.697,
'eval_steps_per_second': 1.386}
Сохранение модели и токенизатора:
# saving the fine tuned model & tokenizer
model_path = "fake-news-bert-base-uncased"
model.save_pretrained(model_path)
tokenizer.save_pretrained(model_path)
Новая папка, содержащая конфигурацию модели и веса, появится после запуска указанной выше ячейки. Если вы хотите выполнить прогнозирование, вы просто используете from_pretrained()
метод, который мы использовали при загрузке модели, и все готово.
Далее создадим функцию, которая принимает в качестве аргумента текст статьи и возвращает, фейк это или нет:
def get_prediction(text, convert_to_label=False):
# prepare our text into tokenized sequence
inputs = tokenizer(text, padding=True, truncation=True, max_length=max_length, return_tensors="pt").to("cuda")
# perform inference to our model
outputs = model(**inputs)
# get output probabilities by doing softmax
probs = outputs[0].softmax(1)
# executing argmax function to get the candidate label
d = {
0: "reliable",
1: "fake"
}
if convert_to_label:
return d[int(probs.argmax())]
else:
return int(probs.argmax())
Я взял пример из test.csv
того, что модель никогда не делала вывод, я проверил его, и это реальная статья из The New York Times:
real_news = """
Tim Tebow Will Attempt Another Comeback, This Time in Baseball - The New York Times",Daniel Victor,"If at first you don’t succeed, try a different sport. Tim Tebow, who was a Heisman quarterback at the University of Florida but was unable to hold an N. F. L. job, is pursuing a career in Major League Baseball. <SNIPPED>
"""
Исходный текст находится в среде Colab , если вы хотите его скопировать, так как это полная статья. Давайте передадим его в модель и посмотрим на результаты:
get_prediction(real_news, convert_to_label=True)
Выход:
reliable
В этом разделе мы предскажем все статьи в test.csv
файле отправки, чтобы увидеть нашу точность в тестовом наборе на конкурсе Kaggle :
# read the test set
test_df = pd.read_csv("test.csv")
# make a copy of the testing set
new_df = test_df.copy()
# add a new column that contains the author, title and article content
new_df["new_text"] = new_df["author"].astype(str) + " : " + new_df["title"].astype(str) + " - " + new_df["text"].astype(str)
# get the prediction of all the test set
new_df["label"] = new_df["new_text"].apply(get_prediction)
# make the submission file
final_df = new_df[["id", "label"]]
final_df.to_csv("submit_final.csv", index=False)
После того, как мы объединим автора, заголовок и текст статьи, мы передаем get_prediction()
функцию в новый столбец, чтобы заполнить label
столбец, а затем используем to_csv()
метод для создания файла отправки для Kaggle. Вот моя оценка подачи:
Мы получили точность 99,78% и 100% в частных и публичных списках лидеров. Это потрясающе!
Хорошо, мы закончили с учебником. Вы можете проверить эту страницу , чтобы увидеть различные параметры тренировки, которые вы можете настроить.
Если у вас есть собственный набор данных фальшивых новостей для тонкой настройки, вам просто нужно передать список образцов в токенизатор, как это сделали мы, после этого вы не будете изменять какой-либо другой код.