日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

input不可编辑属性_谁不喜欢图文并茂呢:基于多模态信息的属性抽取

發布時間:2023/12/2 编程问答 38 豆豆
生活随笔 收集整理的這篇文章主要介紹了 input不可编辑属性_谁不喜欢图文并茂呢:基于多模态信息的属性抽取 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

0. 前言

最近做屬性抽取,并且基于多模態信息(文本+圖片)那種,然后發現了一個比較經典的論文“Multimodal Attribute Extraction”。正好就順著這個論文的思路,捋一下這個任務,復現一下,再記錄一下實踐過程和想法

論文在 2017 年,提出了這么一個做多模態任務的框架,在現在來看感覺還挺經典的,雖然具體方法放到今天來看平平無奇,沒什么高端操作,比不起現在的論文里各種眼花繚亂一通亂秀。但是出發總要從原點開始,中間有些想法和總結,有點什么就記點什么

(NIPS 2017) Multimodal Attribute Extraction?arxiv.org

1. 問題描述

什么是屬性抽取:現實世界的任何事物,都要靠若干屬性來修飾和描述,比如你買了一個肉夾饃,它的“肉料”是“牛肉”還是“豬肉”,它的“口味”是“麻辣”還是“不辣”,都需要被描述清楚,你才能做出決定想不想吃,要不要買

那到哪去獲取這些“屬性描述”呢?你可以看看菜單上寫沒寫,也可以直接問老板,簡單直接,但是這些“原始數據”不總是完美的。萬一老板今天心情不好,不樂意跟你說話,怎么辦。你就需要靠自己。你往冷柜一看,上面寫著是“牛肉”的,再往調料盤一看,沒有辣椒粉的罐子,就推理出應該是“不辣”的。挺好,開心地點了一個吃,做了一次成功的“屬性抽取”,被自己的機智所折服

但是也有可能,你吃著發現,是辣的,吐了。原來老板把辣椒粉裝在別的罐子里了。這個故事告訴我們,“屬性抽取”也是有風險的,因為原始數據也是有噪聲的

怎么做屬性抽取:兩種方式,“抽取式”和“生成式”。直接上圖

抽取式:就是抽取輸入文本中的字詞,組成預測的屬性值。也就是說,預測出的屬性值一定要在輸入側出現過

實現上,可以用上圖所示的序列標注的方式,也可以做成類似 SQuAD 那種的 QA 問題,把屬性作為 query,把屬性值作為 answer,最終輸出兩個 index,取原文中兩個 index 之間的字詞作為屬性值

另外,用序列標注的方式做的話,也有不同的實現方式,比如不把待預測的屬性名(Query Attribute)作為輸入,而是作為輸出。也就是說,不告訴模型想預測哪個屬性,模型你覺得你能預測到哪個屬性就輸出哪個屬性。這種實現方式也包括幾種,比如可以做成一個多任務學習,一個子任務做分類預測屬性名,一個子任務做序列標注標記屬性值;也可以只做一個序列標注任務,屬性名靠標簽本身標記,例如把原本的“B”標簽擴展成“B-口味”“B-肉料”等多個標簽,每個標簽對應一種屬性。五花八門,百川入海,殊途同歸。各種方式都有人做,具體文章不一一列舉了

列一個最近看到抽取式方法的論文吧,方法非常簡單明了,一眼就懂

Scaling Up Open Tagging from Tens to Thousands: Comprehension Empowered Attribute Value Extraction from Product Title. ACL2019

生成式:就是直接生成屬性值,而這個屬性值不一定在輸入文本中出現,只要模型在訓練數據中見過就 ok

實現上,可以直接在一個“大而完整的屬性詞表”上做分類任務,也可以用 seq2seq 的方式把屬性值分割成字詞序列輸出。但是無論如何,都是需要一個“大而完整的屬性詞表”,也就是屬性值一定是可枚舉可窮盡的。代表論文就比如最開始提到的那篇

兩種方式優缺點比較:

抽取式

  • 只能抽取在輸入文本中出現過的屬性值,導致很多屬性不可做
  • 預測屬性值一定在輸入中出現過,具有一定可解釋性,準確性也更高,不會亂預測

生成式

  • 只能預測可枚舉的高頻屬性,導致很多屬性值不可做
  • 預測出來的屬性值沒有可解釋性,在實際業務中,預測出來結果也不一定敢用(比如模型預測出來這個肉夾饃用的是牛肉,但是你也不一定敢信,又看不出來,萬一是假的)

當然也可以融各自所長,做一個既能抽取又能生成的模型:比如基于 CopyNet 或者 Pointer-Generator,針對不同的輸入情況,可以自由在“抽取模式”和“生成模式”之間進行切換。感覺聽上去還比較高級吧,相關文章沒看到過,感興趣可以試一下,一條光明的小道指給你們了,還沒畢業的可以拿去發論文哈,不用客氣

背景故事就講這些吧。下面就是具體的模型和實踐過程

代碼地址

  • 數據集:MAE-dataset https://rloganiv.github.io/mae/
  • 環境:Python3.6, Tensorflow 1.12
  • 代碼:
wavewangyue/mae?github.com

原始數據文件太大了,包括文本信息與圖片文件,需要的話可以自己下載,把下好的數據解壓到 data/origin/train 以及 data/origin/test 目錄下,然后通過 data/process_data.py 把數據處理后生成到 data/train 和 data/test 下即可

但是出于客戶為先的服務態度,我隨機 sample 了一些數據留在了 data/train 以及 data/test 目錄下,不用費力去下載原始數據就能跑 demo,就是為了給你們那種 git clone 完就能原地起飛的暢爽體驗

不用客氣

2. 模型及實現

先講模型,最后說數據處理

模型很簡單小巧,看圖,一眼就懂

用 CNN 得到文本的編碼向量;用預訓練的 VGG 提取圖片的特征向量,作為輸入,然后接一層全連接層將得到圖片的編碼向量;直接用 Embedding 矩陣編碼待預測屬性;最后用一個融合層(Fusion Layer)將以上三個向量融合在一起,再接一層全連接,就得到預測的屬性值了

然后上代碼,先說輸入

self.inputs_seq = tf.placeholder(tf.int32, [None, None], name="inputs_seq") self.inputs_seq_len = tf.placeholder(tf.int32, [None], name="inputs_seq_len") self.inputs_image = tf.placeholder(tf.float32, [None, 3, img_embedding_size], name="inputs_image") self.inputs_attr = tf.placeholder(tf.int32, [None], name="inputs_attr") self.outputs_value = tf.placeholder(tf.int32, [None], name='outputs_value')

這里就提一下 inputs_image,這個是輸入的圖片信息,每個商品最多 3 張圖片,每張圖片被VGG 預先提取為一個 4096 維(img_embedding_size=4096)的特征向量,具體提取方法后面數據處理部分再說

接下來就是按照圖上的結構搭模型

with tf.variable_scope('word_embedding'):embedding_matrix = tf.get_variable("embedding_matrix", [vocab_size_word, txt_embedding_size], dtype=tf.float32)txt_embedded = tf.nn.embedding_lookup(embedding_matrix, self.inputs_seq) # B * S * Dwith tf.variable_scope('txt_encoder'):conv = tf.layers.conv1d(txt_embedded,filters=txt_hidden_size,kernel_size=5,strides=1,padding="valid") # B * (S-5+1) * Dpool = tf.reduce_max(conv, axis=1)pool_drop = tf.layers.dropout(pool, 0.5)txt_contents = tf.layers.dense(pool_drop, txt_hidden_size, activation="relu") # B * Dwith tf.variable_scope('img_encoder'):img_input = tf.layers.dropout(self.inputs_image, 0.5)img_embedded = tf.layers.dense(img_input, img_hidden_size, activation="relu") # B * 3 * Dimg_contents = tf.reduce_max(img_embedded, axis=1) # B * Dwith tf.variable_scope('attr_encoder'):embedding_matrix = tf.get_variable("embedding_matrix", [vocab_size_attr, attr_embedding_size], dtype=tf.float32)attr_contents = tf.nn.embedding_lookup(embedding_matrix, self.inputs_attr) # B * Dwith tf.variable_scope('fusion_layer'):if fusion_mode == "concat":#fusion_all = tf.concat([txt_contents, img_contents, attr_contents], axis=1)fusion_all = tf.concat([txt_contents, attr_contents], axis=1)fusion_contents = tf.layers.dense(fusion_all, fusion_hidden_size) # B * Delif fusion_mode == "gmu":fusion_ta = tf.concat([txt_contents, attr_contents], axis=1)fusion_contents_ta = tf.layers.dense(fusion_ta, fusion_hidden_size) # B * Dfusion_ia = tf.concat([img_contents, attr_contents], axis=1)fusion_contents_ia = tf.layers.dense(fusion_ia, fusion_hidden_size) # B * Dfusion_ti = tf.concat([txt_contents, img_contents], axis=1)gate = tf.nn.sigmoid(tf.layers.dense(fusion_ti, 1)) # B * 1fusion_contents = gate * fusion_contents_ta + (1 - gate) * fusion_contents_ia # B * D

這里說下這個多模態融合層(Fusion Layer)。相比單獨的 NLP 任務或者 CV 任務,多模態任務也沒什么特別的地方,無非是多了一項輸入。所以,它的亮點就在于,多模態信息之間如何進行“交互”。把它倆“交”好了,讓人眼前一亮,就能讓人覺得你這個新穎、高級、給力

現在常用的交互方式有很多,比如(1)直接 concat 然后接一層全連接(2)二者之間做一層 Attention(3)類似 VL-BERT 那種,把文本輸入和圖片輸入前后拼接在一起,通過 Transformer 去學他們之間的關系,本質上還是學 Attention,等等的

這篇論文里還提供了一種方式:gated multimodal unit(gmu),如代碼中所示。這個 gmu 還挺有意思的。商品的有些屬性可以通過圖片看出來(比如顏色),而有些屬性不行,必須要從文字中讀出來(比如內存大小),這個 gmu 就是基于 gate 機制,讓模型自己去學,什么時候看圖,什么時候讀字。實現上也比較簡單,看圖就懂

這種 gate 機制聽上去就很高級。但是就這個數據集上而言,實際效果遠不如直接 concat。任憑你花里胡哨,不如我簡單粗暴

還有一些其他的交互方式,比如我們最近在做的一種,展示效果還是挺炫的。本質上還是計算 Attention,但不是把圖片整個壓縮成一整個向量,而是分成不同區域,保留每個區域的表征向量,這樣在文本中提到某個屬性時,可以觀察模型是否可以在圖片上找到這個屬性對應的區域。比如下圖里,文本里提到“小翻領”,模型就更加關注圖片上衣領那塊區域。分享一下圖一樂,具體細節不細講跑題了

Multimodal Joint Attribute Prediction and Value Extraction for E-commerce Product. Zhu T, Wang Y, Li H, et al. EMNLP 2020.

接下來是 loss。loss 本身沒啥可講的,但是這里還有點意思。這篇論文用的是基于“余弦相似度”的 loss,一般常用的是“交叉熵”loss。兩者公式不一樣,但是出發點都是相同的,感覺應該沒什么區別。但是從實驗結果來看,有時候 loss 的影響可太大了

先上兩種方式的實現代碼

# D 是 predicted value 的向量維度 # V 是全部 value 的詞表大小 distribute_matrix = tf.get_variable("distribute_matrix", [fusion_hidden_size, vocab_size_value], dtype=tf.float32) # D * V# 下面是用 cos_similarity 來得到 predicted values 以及計算 loss dot_part1 = tf.expand_dims(fusion_contents, axis=-1) # B * D * 1 dot_part2 = tf.expand_dims(distribute_matrix, axis=0) # 1 * D * V dot = tf.reduce_sum(dot_part1 * dot_part2, axis=1) # B * V norm_part1 = tf.expand_dims(tf.sqrt(tf.reduce_sum(fusion_contents * fusion_contents, axis=1)), axis=-1) # B * 1 norm_part2 = tf.expand_dims(tf.sqrt(tf.reduce_sum(distribute_matrix * distribute_matrix, axis=0)), axis=0) # 1 * V norm = norm_part1 * norm_part2 # B * V norm = tf.clip_by_value(norm, clip_value_min=1e-8, clip_value_max=1e5) # 截斷一下防止分母為0 sims = dot / norm # B * V # 即 predicted value 和全部詞表中 value 的余弦相似度結果 outputs = tf.argmax(sims, 1, name="outputs") # outputs, 即最終的 predicted values pos_value_onehot = tf.one_hot(self.outputs_value, vocab_size_value) # B * V loss_pos = sims * pos_value_onehot # B * V loss_pos = tf.square(1 - tf.reduce_sum(loss_pos, axis=1)) # 正例的 loss loss_neg = sims * (1 - pos_value_onehot) # B * V loss_neg = tf.clip_by_value(loss_neg, clip_value_min=0, clip_value_max=1) # B * V loss_neg = tf.reduce_sum(tf.square(loss_neg), axis=1) # 負例的 loss loss = loss_pos + loss_neg # B * V loss = tf.reduce_mean(loss) # B# 下面是用 cross_entropy 來得到 predicted values 以及計算 loss logits = tf.matmul(fusion_contents, distribute_matrix) # B * V probs = tf.nn.softmax(logits, name="probs") # B * V outputs = tf.argmax(probs, 1, name="outputs") # outputs, 即最終的 predicted values # loss = tf.nn.sparse_softmax_cross_entropy_with_logits(logits=logits, labels=self.outputs_value) pos_value_onehot = tf.one_hot(self.outputs_value, vocab_size_value) # B * V probs = tf.clip_by_value(p, clip_value_min=1e-8, clip_value_max=1) # 截斷一下防止log里為0 loss = -1 * tf.reduce_sum(pos_value_onehot * tf.log(probs), axis=-1) # B * V loss = tf.reduce_mean(loss) # B

distribute_matrix 是一個 D * V 的矩陣,放在余弦相似度那里,可以理解成把詞表中每個 value 做成一個 D 維的向量,儲存在整個矩陣中,然后逐個去跟預測出的 value vector 計算相似度,看哪個相似度最高就選哪個;放在交叉熵那里,可以理解成一個 dense 層,把預測出的 value vector 映射到 V 維的概率分布上,每一維對應詞表中一個 value 的概率

交叉熵這里是手寫的,便于理解,實際應用中可以直接換成 tf 現成的函數 sparse_softmax_cross_entropy_with_logits,效果要好一些,里面做了很多優化的

這里為啥要提一下這個 loss 呢,因為兩種 loss 感覺上出發點都是一樣的,都是提高正例上的相似度或概率,打壓負例,應該區別不大。論文里是每條數據只采樣一個負例,聽上去不太合理,我試了一下效果也不好,然后就把所有的負例 loss 都算進來(reduce_sum那里)。但是實驗結果顯示,交叉熵要好太多了,學習速度也不是一個量級的

至于啥原因,很難講,可能是機器玄學吧。有可能是交叉熵這種 y*log(p) 的形式本身非常適合分類任務,也可能是計算交叉熵前的 softmax 非常適合反向傳播。我亂猜的。有機會請交叉熵喝酒讓他親自給我嘮一嘮

單方面宣布交叉熵是 loss 屆的王,萬物皆可交叉熵

最后上一下實驗結果對比,直接引用論文里的表了。單用圖片的話(Image Baseline),效果還比不上直接寫規則(Most-Common Value)。文字+圖片的話(Multimodal Baseline)效果會比單用文字(Text Baseline)好一點點點點。說明核心還是文字,圖片只是配角

由此出發再多說一句,不了解啊,瞎猜,是不是現在有很多的多模態任務,把圖片或者視頻或者語音的信息引入之后,效果上并沒有什么明顯提升,其實都是表面光鮮的配角

但是高級就完事了。秀就完事了

3. 數據預處理

關于數據怎么預處理,都可以從代碼里找到,感興趣的話看一眼代碼就可以

這里主要提一下怎么對圖片進行預處理。現在一般多模態任務都會先用一個預訓練模型把圖片提取為特征向量吧,如果把原始圖片作為輸入的話,模型還要另外搭好多層 CNN 去學習圖片特征提取,對模型來說壓力太大了。預訓練模型可以用 VGG 或者 Resnet 或者其他的,可以直接把一張圖片直接壓縮成一個 D 維向量,也可以把圖片分割為 Nx * Ny 塊,輸出形狀為(Nx * Ny * D)的結果

具體實現方式我沒有找到 tensorflow 版本的,就用的 pytorch 官方提供的接口torchvision.models,網上可以直接搜到教程就不細說了。運行代碼時會自動下載預訓練好的模型,如果下載較慢可以本地下好然后傳到它的默認下載目錄下,一般是 ~/.torch/models 這種目錄,具體路徑可以看輸出提示

然后假設一共有 N 張圖片,分別用預訓練模型跑一邊得到一個 N*D 的矩陣,儲存下來。然后訓練的時候,按照每條輸入數據對應的 image index 去矩陣中取,比如第一個商品有 3 張圖片,分別是第 1,3,128 號圖片,就去從這個矩陣中抽

大家如果平時在預處理或者訓練過程中,有更科學的數據處理方式,非常歡迎指導交流,普渡眾生,善莫大焉,獨樂樂不如眾樂樂

就這樣,打完收工

創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎

總結

以上是生活随笔為你收集整理的input不可编辑属性_谁不喜欢图文并茂呢:基于多模态信息的属性抽取的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。