指南將引導你學習構建一個自動完成任意輸入文本的Web應用程序。
作者 | Dev Sharma
譯者 | 蘇本如,責編 | 郭芮
出品 | CSDN(ID:CSDNnews)
以下為譯文:
在本文中,我們將使用OpenAI的新一代表語言模型GPT-2來建立模型,使用Panel框架來構建web頁面的儀表板。本指南分為兩部分。在第一部分中,我們將加載我們的模型并編寫一個預測函數。在第二部分中,我們將構建一個web應用程序。
自動生成文本的應用程序示例。我們將構建這個web應用程序的一個更簡單的變體。
準備工作
本教程假設你已經安裝了Python 3.7+,并且對語言模型有一定的了解。盡管本教程所涉及的所有步驟都可以在Jupyter之外完成,但是強烈建議你使用jupyter notebook。
我們將使用PyTorch作為我們選擇的深度學習庫。在PyTorch中,我們將使用transformers庫導入預先訓練好的OpenGPT-2模型。可以通過在bash命令行中分別輸入以下命令來安裝這些庫:
pip install torch
pip install transformers
對于我們的web應用程序,我們將利用Panel這個窗口容器/框架,這是一個很好的工具,可以被用來從jupyter notebooks或者常規的Python腳本中輕松地創建可維護的儀表板。可以使用以下命令安裝Panel:
pip install panel
第一部分:建立模型
OpenAI的GPT是一種基于transformer的語言模型,它在生成類似人類語言的文本方面受到了很多關注。如果你以前沒有嘗試過,你很可能會在閱讀結束時得出同樣的觀點。
加載模型
首先,我們需要導入所需的包。詳情如下:
import numpy as np
import torch
import torch.nn.functional as F
from transformers import GPT2Tokenizer, GPT2LMHeadModel
from random import choice
接下來,我們將加載OpenGPT2的Tokenizer和語言模型:(如果是第一次運行,可能需要幾分鐘下載預先訓練的模型)
tok=GPT2Tokenizer.from_pretrained("gpt2")
model=GPT2LMHeadModel.from_pretrained("gpt2")
預測函數
在這個階段,大部分工作已經完成了。因為我們的模型是預先訓練過的,所以我們不需要再次訓練它或做任何修改。我們只需要編寫一個函數,它可以向模型輸入文本并生成預測的文本。
def get_pred(text, model, tok, p=0.7):
input_ids=torch.tensor(tok.encode(text)).unsqueeze(0)
logits=model(input_ids)[0][:, -1]
probs=F.softmax(logits, dim=-1).squeeze
idxs=torch.argsort(probs, descending=True)
res, cumsum=, 0.
for idx in idxs:
res.append(idx)
cumsum +=probs[idx]
if cumsum > p:
pred_idx=idxs.new_tensor([choice(res)])
break
pred=tok.convert_ids_to_tokens(int(pred_idx))
return tok.convert_tokens_to_string(pred)
這個函數中發生了很多事情。因此,讓我們把它分解來看看。首先,我們對input_ids中的輸入文本進行標記(tokenize)和編碼(encode)。接著,我們要求我們的模型為下一個單詞/標記(token)生成一個logits向量。在應用softmax函數并按降序對這些可能的概率結果進行排序之后,我們得到了一個向量idxs,它按各自的概率順序列出了每個token的索引。
在這個階段,我們可以選擇概率最高的token。但是,我們希望能夠混合結果,以便相同的輸入文本可以生成各種文本。為此,我們將添加一個隨機元素,從最可能的下一個token列表中選擇一個隨機token。這樣的話,我們就不會每次都選擇相同的預測token。為了做到這一點,我們采用了Nucleus (Top-p) Sampling 方式。
我們通過循環遍歷每個概率來執行此操作,直到循環遍歷的所有概率之和大于p(這里的p是一個介于0到1之間的任意數字)。當p被超過前,所有遍歷到的token都將被存儲在列表res中。一旦p被超過,我們就從這個列表中隨機選擇一個token。請記住,我們正在遍歷的概率列表包含了其按概率排序的索引。注意,p值越高,我們的列表中將包含更多的token。反之亦然。因此,如果每次都希望得到相同的結果,可以將p值設置為0。
現在,讓我們測試一下我們的預測函數:
每次都會有不同的結果,這正是我們所期望的。現在,我們的預測功能準備好了。讓我們開始構建我們的Web應用程序吧!
第二部分:構建web應用程序
Panel框架簡介
如果你還不熟悉Panel框架,那請記住,Panel框架可以幫助我們創建一個web儀表板和web應用程序。簡而言之,你需要知道的是它有三個主要組件:
Panel:可以包含一個或多個窗格(pane)對象的容器,面板(pane)對象是指文本、圖像、圖形、小部件等(也可以包含其他panel);
Pane:任何單個對象,例如文本、圖像、數據幀等;
Widget(小部件):用戶可以自行調整的項目,包括文本輸入框、滑塊、按鈕、復選框,等等可以改變窗格的行為的小部件。
下一個也是最后一個你需要知道的事情是:我們有多種方法來定義不同的窗格和小部件之間的交互方式,我們稱之為“callback(回調)”。例如,如果按下某個按鈕,其他窗格應該如何更新呢?稍后我們將定義一個回調函數來演示它將如何準確地做到這一點。
Web應用程序簡介
我們的文本生成器應用程序將有一個輸入窗口,以便用戶輸入他們想要輸入的文本。接下來,用戶應該能夠通過按下按鈕生成新的token。在這之后,它將使用我們在第一部分中定義的預測函數來預測新的token來生成新文本。最后,用戶應該能夠在已經預測的token基礎之上繼續生成新的文本。
實施
讓我們首先導入Panel并創建文本輸入小部件:
import panel as pn
pn.extension # loading panel's extension for jupyter compatibility text_input=pn.widgets.TextInput
現在,如果在jupyter中執行文本輸入,我們將得到以下結果:
接下來,我們需要一個窗格,它會在越來越多的token生成時存儲整個文本:
generated_text=pn.pane.Markdown(object=text_input.value)
注意,這里我們將文本對象設置為text_input的值。我們希望generated_text的值與text_input的值相同,因為我們將要在generated_text之上預測新文本。隨著越來越多的token被添加到我們的序列中,我們將繼續基于generated_text進行預測,直到用戶改變了text_input。一旦用戶改變了text_input,這個進程將重新啟動。
然而,到這里事情還沒有完全結束。盡管generated_text將在開始時接受text_input的值,但如果text_input值發生更改,generated_text值將不會自我更新。為此,我們需要像下面這樣將這兩個對象鏈接在一起:
text_input.link(generated_text, value='object')
這里,我們在text_input和generated_text之間形成了單向鏈接。因此,每當text_input的值發生更改時,generated_text值也將更改為新值。如下所示:
觀察面板中文的text_input和generated_text之間的鏈接行為。注意:作為組件的pn.Row也是一個面板,即它是一個窗格和小部件的容器。
現在我們有了兩個文本對象,讓我們來創建按鈕小部件:
button=pn.widgets.Button(name="Generate",button_type="primary")
很好,現在我們有了一個按鈕,我們只需要把它和我們想要的行為鏈接起來。為此,我們將編寫一個回調函數,該函數將在每次單擊按鈕時運行:
def click_cb(event):
pred=get_pred(generated_text.object, model, tok)
generated_text.object +=pred
這里發生了兩件事。首先,我們將generated_text作為輸入傳遞給我們之前編寫的預測函數,該函數將生成一個新的token。其次,將此token添加到generated_text中。每次新單擊按鈕時,這個過程都會重復。
到這里,我們仍然需要將按鈕單擊事件與回調函數綁定在一起。我們可以這樣做:
button.on_click(click_cb)
我們現在已經完成了所有小部件、窗格和函數的創建。接下來我們需要做的只是把這些東西放在一個面板里,然后看看會發現什么:
app=pn.Column(text_input, button, generated_text); app
注:pn.Column與pn.Row類似,它是另一種類型的panel,即小部件、窗格甚至其他panel的容器。
讓我們再添加一個標題和一個簡短的描述,我們就大功告成了!
title=pn.pane.Markdown("# **Text Generator**")
desc=pn.pane.HTML("<marquee scrollamount='10'><b>Welcome to the text generator! In order to get started, simply enter some starting input text below, click generate a few times and watch it go!</b></marquee>")final_app=pn.Column(title, desc ,app)
服務你的Web應用程序
Panel框架使得服務web應用程序變得非常容易。有兩種方法可以用來做這件事。第一個是調用“.show”命令。這種方法通常用于調試,如下面所示。它將啟動一個新窗口,在這個窗口中,一個名為final_app的 panel將作為一個web應用程序運行。
final_app.show
而為了讓它在生產環境中運行,你需要使用“.servable”方法。但是,如果你以類似于show方法的方式運行此操作,你的筆記本上不會出現任何東西,你必須像下面這樣在你的筆記本上運行bash腳本:
panel serve --show text_generation_app.ipynb
只要你的筆記本中有以下代碼,這個操作將在本地端口上啟動你的web應用:
final_app.servable
大功告成!
現在,你自己有能力構建一個自動生成文本的應用程序。你可以通過添加更多panel組件來進一步完善它。你甚至可以將此應用程序嵌入到其他項目中。像往常一樣,你可以在github上找到我的代碼庫。注意:下面圖片中的app是我在本教程中使用的app(text_generation_app.ipynb)的高級變體:。
原文:https://towardsdatascience.com/build-a-text-generator-web-app-in-under-50-lines-of-python-9b63d47edabb
本文為 CSDN 翻譯,轉載請注明來源出處。
【End】
:root
:root 這個 CSS 偽類匹配文檔樹的根元素。對于 HTML 來說,:root 表示元素,除了優先級更高之外,與 html 選擇器相同。
在聲明全局 CSS 變量時 :root 會很有用:
:root {
--main-color: hotpink;
--pane-padding: 5px 42px;
}
place-items
CSS 中的 place-items 是一個簡寫屬性 ,它允許我們在相關的布局(如 Grid 或 Flexbox)中可以同時沿著塊級和內聯方向對齊元素 (例如:align-items 和 justify-items 屬性)。
如果未提供第二個值,則第一個值作為第二個值的默認值。
也就是說,以后需要定義水平垂直居中的盒子就不需要再 justify-items: center 和 align-items: center 了,直接一行搞定,看起來更加專業。
background-clip
background-clip 設置元素的背景(背景圖片或顏色)是否延伸到邊框、內邊距盒子、內容盒子下面。
值
linear-gradient()
CSS linear-gradient() 函數用于創建一個表示兩種或多種顏色線性漸變的圖片。
其結果屬于 < gradient > 數據類型,是一種特別的 < image > 數據類型。
radial-gradient()
radial-gradient() CSS 函數創建了一個圖像,該圖像是由從原點發出的兩種或者多種顏色之間的逐步過渡組成。它的形狀可以是圓形(circle)或橢圓形(ellipse)。
這個方法得到的是一個 CSS < gradient > 數據類型的對象,是 < image > 的一種。
實現效果大致如下:
css 樣式如下:
* {
box-sizing: border-box;
}
/* :root 這個 CSS 偽類匹配文檔樹的根元素。對于 HTML 來說,:root 表示 <html> 元素,除了優先級更高之外,與 html 選擇器相同。*/
/* 在聲明全局 CSS 變量 */
:root {
--color-1: #186cb8;
--color-2: #2a9a9f;
--color-3: #f1b211;
--color-4: #e83611;
--color-5: #f9002f;
}
.wrapper {
background: #000;
line-height: 1;
min-height: 100%;
display: grid;
place-items: center;
min-height: calc(100vh - 16px);
}
h1 {
font-family: "Exo", sans-serif;
font-size: 15vw;
font-weight: 900;
width: -webkit-min-content;
width: -moz-min-content;
width: min-content;
margin: auto;
text-transform: uppercase;
background: linear-gradient(
219deg,
var(--color-1) 19%,
transparent 19%,
transparent 20%,
var(--color-2) 20%,
var(--color-2) 39%,
transparent 39%,
transparent 40%,
var(--color-3) 40%,
var(--color-3) 59%,
transparent 59%,
transparent 60%,
var(--color-4) 60%,
var(--color-4) 79%,
transparent 79%,
transparent 80%,
var(--color-5) 80%
);
background-clip: text;
-webkit-background-clip: text;
color: transparent;
}
.container {
padding: 1.5rem;
text-align: center;
background: radial-gradient(
circle at 1.4% 1.4%,
var(--color-1) 0.8%,
transparent 0.8%
),
radial-gradient(
circle at 5.5% 3%,
var(--color-2) 0.45%,
transparent 0.45%
),
radial-gradient(
circle at 2.5% 3.5%,
var(--color-3) 0.5%,
transparent 0.5%
),
radial-gradient(
circle at 4.5% 1.2%,
var(--color-4) 0.25%,
transparent 0.25%
),
radial-gradient(
circle at 98% 98%,
var(--color-1) 0.8%,
transparent 0.8%
),
radial-gradient(
circle at 95% 95%,
var(--color-2) 0.45%,
transparent 0.45%
),
radial-gradient(
circle at 94.5% 97.5%,
var(--color-3) 0.5%,
transparent 0.5%
),
radial-gradient(
circle at 98.5% 95.5%,
var(--color-4) 0.25%,
transparent 0.25%
);
}
@media screen and (min-width: 768px) {
h1 {
font-size: 6.5rem;
}
}
html 標簽結構如下:
<divclass="wrapper">
<divclass="container">
<h1>multi color text with css</h1>
</div>
</div>
繪畫基礎圖案不難,重點都在于背景顏色的繪制,重點研究下背景代碼。
下期給大家分享更多實戰中的點滴,如果大家對此感興趣,歡迎各位關注、留言,大家的支持就是我的動力!
最近在做系統的UI優化,美工設計了一版系統主頁的消息Tabs標簽,我感覺直接使用BootStrap的原裝控件就好了,但是做出來后領導們審查沒通過,說要和美工效果一致,好吧,那就按領導的意思做吧,先和大家看一下效果:
這個是我做的效果,但是領導們不滿意;
這個是美工給的效果圖,咱們最終也按照這個效果圖為準;
好了說干就干,仔細想了一下如果重新去寫一個Tabs的插件也挺麻煩,還不如在原來的基礎上進行修改,只需要把樣式改掉就可以了,把樣式重新放在一個新的文件中就可以了,好了,直接上代碼:
首先是引用,需要把BootStrap和fontawesome引用進來,說明一下:
BootStrap主要是tabs的插件;
Fontawesome他是一個圖標庫,主要用于系統中使用的圖標
<link rel="stylesheet" type="text/css" href="resource/bootstrap-3.3.5/css/bootstrap.css" />
<link type="text/css" rel="stylesheet" href="resource/bootstrap-3.3.5/css/font-awesome.min.css">
<script type="text/javascript" src="resource/bootstrap-3.3.5/js/bootstrap.min.js"></script>
<div class="msgContent">
<div class="msgContent_link"></div>
<ul id="myTab" class="nav nav-tabs">
<li class="active">
<a href="#msgInbox" data-toggle="tab">
收件箱
</a>
</li>
<li>
<a href="#msgOutBox" data-toggle="tab">
發件箱
</a>
</li>
<li>
<a href="#comGroup" data-toggle="tab">
通訊組
</a>
</li>
</ul>
<div id="myTabContent" class="tab-content">
<div class="tab-pane fade in active" id="msgInbox" style="padding-bottom: 10px;overflow: auto;max-height: 500px;">
<div id="msgInbox_content">
</div>
<div class="msg_op">
<span class="fa fa-refresh msg_op_refresh" onclick="msgOpRefresh();"></span>
<span class="fa fa-navicon msg_op_list" onclick="selectMenu(' /msg/msg/inboxList.haze','收件箱')"></span>
</div>
</div>
<div class="tab-pane fade" id="msgOutBox" style="padding-bottom: 10px;overflow: auto;max-height: 500px;">
<div id="msgOutbox_content">
</div>
<div class="msg_op">
<span class="msg_send_btn" onclick="sendMsg();">發送消息</span>
<span class="fa fa-refresh msg_op_refresh" onclick="getMsgOutBoxList();"></span>
<span class="fa fa-navicon msg_op_list" onclick="selectMenu('/msg/msg/outboxList','發件箱')"></span>
</div>
</div>
<div class="tab-pane fade" id="comGroup" style="">
<p>這是通訊組</p>
</div>
</div>
</div>
.msgContent{
position: absolute;
width: 300px;
height: auto;
border: 1px solid #bdb5b5;
background-color: #FFFFFF;
border-radius: 10px;
right: 5px;
padding: 5px;
display:none;
top: 50px;
}
.msgContent .msgContent_link{
position: absolute;
border-right: 16px solid transparent;
border-left: 16px solid transparent;
border-bottom: 16px solid #ffffff;
top: -16px;
left: 193px;
}
#myTab{
height:40px;
border-bottom: none;
}
#myTab li{
height:40px;
width: 33.3%;
}
#myTab>li {
margin-left: -1px;
border-top: none;
border-left: none;
border-right: none;
z-index: 1;
}
#myTab>li.active {
border-top: 0px;
border-left: none;
border-right: none;
z-index: 3;
}
#myTab>li.active>a, #myTab>li.active>a:hover, #myTab>li.active>a:focus {
border-top: none;
border-left: 0px;
border-right: 0px;
border-bottom: 1px solid #FFF;
color: #000;
}
#myTab > li.active > a, #myTab > li.active > a:hover, #myTab > li.active > a:focus {
color: #000;
cursor: default;
background-color: #fff;
/* border: 1px solid #666363; */
border-bottom-color: transparent;
font-weight: bold;
}
#myTab>li>a, #myTab>li>a:focus {
color: #999999;
border-left: 0px;
border-right: 0px;
margin-right: 0px;
padding: 10px 16px;
background: #FBFAF8;
border-bottom: 0px;
font-size: 15px;
font-weight: bold;
}
#myTab>li.active>a::after{
content: "";
width: 20px;
height: 2px;
background: #000;
position: absolute;
left: 36px;
top: 35px;
}
#msgInbox_content{
padding-bottom: 30px;
}
#msgOutbox_content{
padding-bottom: 30px;
}
.msgRow{
width:100%;
height:55px;
border-bottom: 1px solid #cccccc;
padding: 5px;
cursor: pointer;
}
.msgRow:hover{
background-color:#cccccc;
}
.msgRow:hover .msgRow_content_info .icon{
color:#0281CC;
}
.msgRow_content{
width:100%;
}
.msgRow_content_info{
line-height: 20px;
float: left;
}
.msgRow_content_info .icon{
font-size: 20px;
margin-top: 3px;
/* */
}
.msgRow_content_info .name{
margin-left: 10px;
}
.msgRow_content_info .date{
margin-left: 15px;
}
.msgRow_content_info .status{
margin-left: 25px;
padding: 2px;
background-color: rgba(217, 105, 168, 0.25);
border: 1px solid #d969a8;
color: #d969a8;
border-radius: 4px;
}
.msgRow_content_info .status span{
display: inline-block;
transform: scale(0.8);
}
.msgRow_content_info .del{
margin-left: 25px;
cursor: pointer;
font-size: 14px;
margin-top: 3px;
/* */
}
.msgRow_content_con{
line-height: 20px;
float: left;
}
.msgRow_content_con span{
margin-left: 30px;
font-weight: bold;
cursor: pointer;
}
.msg_op{
line-height: 35px;
float: right;
padding-top: 6px;
position: absolute;
bottom: 1px;
background-color: white;
width: 98%;
text-align: right;
}
.msg_op .msg_op_refresh{
margin-right: 20px;
font-size: 16px;
cursor: pointer;
}
.msg_op .msg_op_list{
margin-right: 20px;
font-size: 16px;
cursor: pointer;
}
.msg_send_btn{
background-color: #0281CC;
color: white;
padding: 2px;
cursor: pointer;
float: left;
line-height: 20px;
margin-top: 5px;
margin-left: 5px;
border-radius: 5px;
}
//加載消息列表
function checkUnreadMsg(showTip){
$.ajax({
type:'post',
url:'../msg/msg/getInboxUnreadList.haze',
dataType:'json',
success:function(data){
if(data !=null){
if(data.total > 0){
$("#unreadMsgSpan,#unreadMsgSjxSpan").html(data.total);
$(".home-msg-count").find("div").html(data.total);
}else{
$("#unreadMsgSpan,#unreadMsgSjxSpan").html("0");
$(".home-msg-count").find("div").html("0");
}
$('#msgInbox_content').html("");
var html="";
for(var i=0; i < data.rows.length;i++){
var row=data.rows[i];
html +='<div class="msgRow">';
html +='<div class="msgRow_content">';
html +=' <div class="msgRow_content_info">';
if(row.msgTypeStr=="普通消息"){
html +=' <span class="fa fa-envelope-o icon"></span>';
}
else{
html +=' <span class="fa fa-wpforms icon"></span>';
}
html +=' <span class="name">'+row.readUserName+'</span>';
html +=' <span class="date">'+row.msgSendDateStr.split(' ')[0]+'</span>';
html +=' <span class="status"><span>'+row.msgReadStatuStr+'</span></span>';
html +=' <span class="fa fa-trash-o del" onclick="msgDel('+row.id+')"></span>';
html +=' </div>';
html +=' <div class="msgRow_content_con">';
var content="";
if(row.msgContentTrim.length > 15){
content=row.msgContentTrim.substring(0,15)+"...";
}
else{
content=row.msgContentTrim;
}
if(row.msgTypeStr=="普通消息"){
html +=' <span onclick="msgCheck('+row.id+')">'+content+'</span>';
}
else{
html +=' <span onclick="msgProessHandle('+row.id+','+row.msgBusinessType+')">'+content+'</span>';
}
html +=' </div>';
html +='</div>';
html +='</div>';
}
$('#msgInbox_content').append(html);
}
}
});
}
關于代碼中使用到的其他的js事件代碼,可以根據自己的需求進行更改,在這里就不列出來,本代碼中只列出來的加載消息列表的方法;
*請認真填寫需求信息,我們會在24小時內與您取得聯系。