整合營銷服務商

          電腦端+手機端+微信端=數據同步管理

          免費咨詢熱線:

          2020年文檔相似性算法:初學者教程

          2020年文檔相似性算法:初學者教程

          果你想知道2020年文檔相似性任務的最佳算法,你來對了地方。

          在33914篇《紐約時報》文章中,我測試了5種常見的文檔相似性算法。從傳統的統計方法到現代的深度學習方法。

          每個實現少于50行代碼。所有使用的模型都來自互聯網。因此,你可以在沒有數據科學知識的情況下,開箱即用,并且得到類似的結果。

          在這篇文章中,你將學習如何實現每種算法以及如何選擇最佳算法。內容如下:

          1. 最佳的定義
          2. 實驗目標陳述
          3. 數據設置
          4. 比較標準
          5. 算法設置
          6. 選出贏家
          7. 對初學者的建議

          你想深入自然語言處理和人工智能。你想用相關的建議來增加用戶體驗。你想升級舊的現有算法。那么你會喜歡這個文章的。

          數據科學家主張絕對最好

          你可能會搜索術語“最佳文檔相似性算法”(best document similarity algorithms)。

          然后你將從學術論文,博客,問答中得到搜索結果。一些側重于特定算法的教程,而另一些則側重于理論概述。

          在學術論文中,一個標題說,這種算法的準確率達到了80%,而其他算法的準確率僅為75%。好啊。但是,這種差異是否足以讓我們的眼睛注意到它呢?增加2%怎么樣?實現這個算法有多容易?科學家傾向于在給定的測試集中追求最好,而忽略了實際意義。

          在相關的問題問答中,狂熱的支持者占據了整個話題。有人說現在最好的算法是BERT。這個算法概念是如此具有革命性,它打敗了一切。另一方面,憤世嫉俗者稱一切都取決于工作。有些答案早在深度學習之前就有了。看看這個Stackoverflow(https://stackoverflow.com/questions/8897593/how-to-compute-the-similarity-between-two-text-documents)。2012年是投票最多的一年,很難判斷它對我們到底意味著什么。

          谷歌會很樂意投入數百萬美元購買工程師的能力和最新的計算能力,僅僅是為了將他們的搜索能力提高1%。這對我們來說可能既不現實也沒有意義。

          性能增益和實現所需的技術專業知識之間有什么權衡?它需要多少內存?它以最少的預處理可以運行多快?

          你想知道的是一種算法在實際意義上是如何優于另一種算法的。

          這篇文章將為你提供一個指導方針,指導你在文檔相似性問題應該實現哪種算法。

          各種算法,通篇流行文章,預訓練模型

          本實驗有4個目標:

          1. 通過在同一個數據集上運行多個算法,你將看到算法與另一個算法的公平性以及公平程度。
          2. 通過使用來自流行媒體的全文文章作為我們的數據集,你將發現實際應用程序的有效性。
          3. 通過訪問文章url,你將能夠比較結果質量的差異。
          4. 通過只使用公開可用的預訓練模型,你將能夠設置自己的文檔相似性并得到類似的輸出。

          “預訓練模型是你的朋友。-Cathal Horan”

          數據設置-5篇基礎文章

          本實驗選取了33914篇《紐約時報》的文章。從2018年到2020年6月。數據主要是從RSS中收集的,文章的平均長度是6500個字符。

          從這些文章中選擇5個作為相似性搜索的基礎文章。每一個代表一個不同的類別。

          在語義類別的基礎上,我們還將度量書面格式。更多的描述在下面。

          1. Lifestyle, Human Interest:How My Worst Date Ever Became My Best(https://www.nytimes.com/2020/02/14/style/modern-love-worst-date-of-my-life-became-best.html)
          2. Science, Informational:A Deep-Sea Magma Monster Gets a Body Scan(https://www.nytimes.com/2019/12/03/science/axial-volcano-mapping.html)
          3. Business, News:Renault and Nissan Try a New Way After Years When Carlos Ghosn Ruled(https://www.nytimes.com/2019/11/29/business/renault-nissan-mitsubishi-alliance.html)
          4. Sports, News:Dominic Thiem Beats Rafael Nadal in Australian Open Quarterfinal(https://www.nytimes.com/2020/01/29/sports/tennis/thiem-nadal-australian-open.html)
          5. Politics, News:2020 Democrats Seek Voters in an Unusual Spot: Fox News(https://www.nytimes.com/2019/04/17/us/politics/fox-news-democrats-2020.html)

          判斷標準

          我們將使用5個標準來判斷相似性的性質。如果你只想查看結果,請跳過此部分。

          1. 標簽的重疊
          2. 小節
          3. 文風
          4. 主題

          標簽是最接近人類判斷內容相似性的工具。記者自己親手寫下標簽。你可以在HTML標題中的news_keywords meta標記處檢查它們。使用標簽最好的部分是我們可以客觀地測量兩個內容有多少重疊。每個標簽的大小從1到12不等。兩篇文章的標簽重疊越多,就越相似。

          第二,我們看這個部分。這就是《紐約時報》在最高級別對文章進行分類的方式:科學、政治、體育等等。在網址的域名后面會進行顯示,例如nytimes.com/…

          第二部分是小節。例如,一個版塊可以細分為world,或者world可以細分為Australia。并不是所有的文章都包含它,它不像以上那2個那么重要。

          第四是文風。大多數文檔比較分析只關注語義。但是,由于我們是在實際用例中比較推薦,所以我們也需要類似的寫作風格。例如,你不想在學術期刊的“跑鞋和矯形術”之后,從商業角度閱讀“十大跑鞋”。我們將根據杰斐遜縣學校的寫作指導原則對文章進行分組。該列表包括人類興趣、個性、最佳(例如:產品評論)、新聞、操作方法、過去的事件和信息。

          5個候選算法

          這些是我們將要研究的算法。

          1. Jaccard
          2. TF-IDF
          3. Doc2vec
          4. USE
          5. BERT

          每一個算法對33914篇文章運行,以找出得分最高的前3篇文章。對于每一篇基礎文章,都會重復這個過程。

          輸入的是文章的全文內容。標題被忽略。

          請注意,有些算法并不是為文檔相似性而構建的。但是在互聯網上有如此不同的意見,我們將親眼看到結果。

          我們將不關注概念理解,也不關注詳細的代碼審查。相反,其目的是展示問題的設置有多簡單。如果你不明白以下算法的細節,不要擔心,你可以閱讀其他優秀博客進行理解

          你可以在Github repo中找到整個代碼庫:https://github.com/massanishi/document_similarity_algorithms_experiments

          如果你只想查看結果,請跳過此部分。

          Jaccard

          Jaccard 在一個多世紀前提出了這個公式。長期以來,這一概念一直是相似性任務的標準。

          幸運的是,你會發現jaccard是最容易理解的算法。數學很簡單,沒有向量化。它可以讓你從頭開始編寫代碼。

          而且,jaccard是少數不使用余弦相似性的算法之一。它標記單詞并計算交集。

          我們使用NLTK對文本進行預處理。

          步驟:

          1. 小寫所有文本
          2. 標識化
          3. 刪除停用詞
          4. 刪除標點符號
          5. 詞根化
          6. 計算兩個文檔中的交集/并集
          import string
          import nltk
          
          nltk.download('stopwords')
          nltk.download('wordnet')
          nltk.download('punkt')
          
          from nltk.corpus import stopwords
          from nltk.tokenize import word_tokenize
          from nltk.stem import WordNetLemmatizer
          
          lemmatizer=WordNetLemmatizer()
          
          base_document="This is an example sentence for the document to be compared"
          documents=["This is the collection of documents to be compared against the base_document"]
          
          def preprocess(text):
              # 步驟:
              # 1. 小寫字母
              # 2. 詞根化
              # 3. 刪除停用詞
              # 4. 刪除標點符號
              # 5. 刪除長度為1的字符
          
              lowered=str.lower(text)
          
              stop_words=set(stopwords.words('english'))
              word_tokens=word_tokenize(lowered)
          
              words=[]
              for w in word_tokens:
                  if w not in stop_words:
                      if w not in string.punctuation:
                          if len(w) > 1:
                              lemmatized=lemmatizer.lemmatize(w)
                              words.append(lemmatized)
          
              return words
          
          def calculate_jaccard(word_tokens1, word_tokens2):
              # 結合這兩個標識來找到并集。
              both_tokens=word_tokens1 + word_tokens2
              union=set(both_tokens)
          
              # 計算交集
              intersection=set()
              for w in word_tokens1:
                  if w in word_tokens2:
                      intersection.add(w)
          
              jaccard_score=len(intersection)/len(union)
              return jaccard_score
          
          def process_jaccard_similarity():
          
              # 標記我們要比較的基本文檔。
              base_tokens=preprocess(base_document)
          
              # 標記每一篇文檔
              all_tokens=[]
              for i, document in enumerate(documents):
                  tokens=preprocess(document)
                  all_tokens.append(tokens)
          
                  print("making word tokens at index:", i)
          
              all_scores=[]
              for tokens in all_tokens:
                  score=calculate_jaccard(base_tokens, tokens)
          
                  all_scores.append(score)
          
              highest_score=0
              highest_score_index=0
              for i, score in enumerate(all_scores):
                  if highest_score < score:
                      highest_score=score
                      highest_score_index=i
          
              most_similar_document=documents[highest_score_index]
          
              print("Most similar document by Jaccard with the score:", most_similar_document, highest_score)
          
          process_jaccard_similarity()
          

          TF-IDF

          這是自1972年以來出現的另一種成熟算法。經過幾十年的測試,它是Elasticsearch的默認搜索實現。

          Scikit learn提供了不錯的TF-IDF的實現。TfidfVectorizer允許任何人嘗試此操作。

          利用scikit-learn的余弦相似度計算TF-IDF詞向量的結果。我們將在其余的例子中使用這種余弦相似性。余弦相似性是許多機器學習任務中使用的一個非常重要的概念,可能值得你花時間熟悉一下。

          多虧了scikit learn,這個算法產生了最短的代碼行。

          from sklearn.feature_extraction.text import TfidfVectorizer
          from sklearn.metrics.pairwise import cosine_similarity
          
          base_document="This is an example sentence for the document to be compared"
          documents=["This is the collection of documents to be compared against the base_document"]
          
          def process_tfidf_similarity():
              vectorizer=TfidfVectorizer()
          
              # 要生成統一的向量,首先需要將兩個文檔合并。
              documents.insert(0, base_document)
              embeddings=vectorizer.fit_transform(documents)
          
              cosine_similarities=cosine_similarity(embeddings[0:1], embeddings[1:]).flatten()
          
              highest_score=0
              highest_score_index=0
              for i, score in enumerate(cosine_similarities):
                  if highest_score < score:
                      highest_score=score
                      highest_score_index=i
          
          
              most_similar_document=documents[highest_score_index]
          
              print("Most similar document by TF-IDF with the score:", most_similar_document, highest_score)
          
          process_tfidf_similarity()
          

          Doc2vec

          Word2vec于2014年面世,這讓當時的開發者們刮目相看。你可能聽說過非常有名的一個例子:

          國王 - 男性=女王

          Word2vec非常擅長理解單個單詞,將整個句子向量化需要很長時間。更不用說整個文件了。

          相反,我們將使用Doc2vec,這是一種類似的嵌入算法,將段落而不是每個單詞向量化。你可以看看這個博客的介紹:https://medium.com/wisio/a-gentle-introduction-to-doc2vec-db3e8c0cce5e

          不幸的是,對于Doc2vec來說,沒有官方預訓練模型。我們將使用其他人的預訓練模型。它是在英文維基百科上訓練的(數字不詳,但模型大小相當于1.5gb):https://github.com/jhlau/doc2vec

          Doc2vec的官方文檔指出,輸入可以是任意長度。一旦標識化,我們輸入整個文檔到gensim庫。

          from gensim.models.doc2vec import Doc2Vec
          from sklearn.metrics.pairwise import cosine_similarity
          
          import string
          import nltk
          
          nltk.download('stopwords')
          nltk.download('wordnet')
          nltk.download('punkt')
          
          from nltk.corpus import stopwords
          from nltk.tokenize import word_tokenize
          from nltk.stem import WordNetLemmatizer
          
          lemmatizer=WordNetLemmatizer()
          
          base_document="This is an example sentence for the document to be compared"
          documents=["This is the collection of documents to be compared against the base_document"]
          
          def preprocess(text):
              # 步驟:
              # 1. 小寫字母
              # 2. 詞根化
              # 3. 刪除停用詞
              # 4. 刪除標點符號
              # 5. 刪除長度為1的字符
          
              lowered=str.lower(text)
          
              stop_words=set(stopwords.words('english'))
              word_tokens=word_tokenize(lowered)
          
              words=[]
              for w in word_tokens:
                  if w not in stop_words:
                      if w not in string.punctuation:
                          if len(w) > 1:
                              lemmatized=lemmatizer.lemmatize(w)
                              words.append(lemmatized)
          
              return words
          
          def process_doc2vec_similarity():
          
              # 這兩種預先訓練的模型都可以在jhlau的公開倉庫中獲得。
              # URL: https://github.com/jhlau/doc2vec
          
              # filename='./models/apnews_dbow/doc2vec.bin'
              filename='./models/enwiki_dbow/doc2vec.bin'
          
              model=Doc2Vec.load(filename)
          
              tokens=preprocess(base_document)
          
              # 只處理出現在doc2vec預訓練過的向量中的單詞。enwiki_ebow模型包含669549個詞匯。
              tokens=list(filter(lambda x: x in model.wv.vocab.keys(), tokens))
          
              base_vector=model.infer_vector(tokens)
          
              vectors=[]
              for i, document in enumerate(documents):
          
                  tokens=preprocess(document)
                  tokens=list(filter(lambda x: x in model.wv.vocab.keys(), tokens))
                  vector=model.infer_vector(tokens)
                  vectors.append(vector)
          
                  print("making vector at index:", i)
          
              scores=cosine_similarity([base_vector], vectors).flatten()
          
              highest_score=0
              highest_score_index=0
              for i, score in enumerate(scores):
                  if highest_score < score:
                      highest_score=score
                      highest_score_index=i
          
              most_similar_document=documents[highest_score_index]
              print("Most similar document by Doc2vec with the score:", most_similar_document, highest_score)
          
          process_doc2vec_similarity()
          

          Universal Sentence Encoder (USE)

          這是Google最近在2018年5月發布的一個流行算法。 實現細節:https://www.tensorflow.org/hub/tutorials/semantic_similarity_with_tf_hub_universal_encoder。

          我們將使用谷歌最新的官方預訓練模型:Universal Sentence Encoder 4(https://tfhub.dev/google/universal-sentence-encoder/4).

          顧名思義,它是用句子來構建的。但官方文件并沒有限制投入規模。沒有什么能阻止我們將它用于文檔比較任務。

          整個文檔按原樣插入到Tensorflow中。沒有進行標識化。

          from sklearn.metrics.pairwise import cosine_similarity
          
          import tensorflow as tf
          import tensorflow_hub as hub
          
          base_document="This is an example sentence for the document to be compared"
          documents=["This is the collection of documents to be compared against the base_document"]
          
          def process_use_similarity():
              filename="./models/universal-sentence-encoder_4"
          
              model=hub.load(filename)
          
              base_embeddings=model([base_document])
          
              embeddings=model(documents)
          
              scores=cosine_similarity(base_embeddings, embeddings).flatten()
          
              highest_score=0
              highest_score_index=0
              for i, score in enumerate(scores):
                  if highest_score < score:
                      highest_score=score
                      highest_score_index=i
          
              most_similar_document=documents[highest_score_index]
              print("Most similar document by USE with the score:", most_similar_document, highest_score)
          
          process_use_similarity()
          

          BERT

          這可是個重量級選手。2018年11月谷歌開源BERT算法。第二年,谷歌搜索副總裁發表了一篇博文,稱BERT是他們過去5年來最大的飛躍。

          它是專門為理解你的搜索查詢而構建的。當談到理解一個句子的上下文時,BERT似乎比這里提到的所有其他技術都要出色。

          最初的BERT任務并不打算處理大量的文本輸入。對于嵌入多個句子,我們將使用UKPLab(來自德國大學)出版的句子轉換器開源項目(https://github.com/UKPLab/sentence-transformers),其計算速度更快。它們還為我們提供了一個與原始模型相當的預訓練模型(https://github.com/UKPLab/sentence-transformers#performance)

          所以每個文檔都被標記成句子。并對結果進行平均,以將文檔表示為一個向量。

          import numpy as np
          from sklearn.metrics.pairwise import cosine_similarity
          from nltk import sent_tokenize
          
          from sentence_transformers import SentenceTransformer
          
          base_document="This is an example sentence for the document to be compared"
          documents=["This is the collection of documents to be compared against the base_document"]
          
          def process_bert_similarity():
              # 這將下載和加載UKPLab提供的預訓練模型。
              model=SentenceTransformer('bert-base-nli-mean-tokens')
          
              # 雖然在句子轉換器的官方文件中并沒有明確的說明,但是原來的BERT是指一個更短的句子。我們將通過句子而不是整個文檔來提供模型。
              sentences=sent_tokenize(base_document)
              base_embeddings_sentences=model.encode(sentences)
              base_embeddings=np.mean(np.array(base_embeddings_sentences), axis=0)
          
              vectors=[]
              for i, document in enumerate(documents):
          
                  sentences=sent_tokenize(document)
                  embeddings_sentences=model.encode(sentences)
                  embeddings=np.mean(np.array(embeddings_sentences), axis=0)
          
                  vectors.append(embeddings)
          
                  print("making vector at index:", i)
          
              scores=cosine_similarity([base_embeddings], vectors).flatten()
          
              highest_score=0
              highest_score_index=0
              for i, score in enumerate(scores):
                  if highest_score < score:
                      highest_score=score
                      highest_score_index=i
          
              most_similar_document=documents[highest_score_index]
              print("Most similar document by BERT with the score:", most_similar_document, highest_score)
          
          process_bert_similarity()
          

          算法評估

          讓我們看看每種算法在我們的5篇不同類型的文章中的表現。我們根據得分最高的三篇文章進行比較。

          在這篇博文中,我們將只介紹五種算法中性能最好的算法的結果。有關完整的結果以及個別文章鏈接,請參閱倉庫中的算法目錄:https://github.com/massanishi/document_similarity_algorithms_experiments

          1. How My Worst Date Ever Became My Best

          BERT勝利

          這篇文章是一個人類感興趣的故事,涉及一個50年代離婚婦女的浪漫約會。

          這種寫作風格沒有像名人名字這樣的特定名詞。它對時間也不敏感。2010年的一個關于人類興趣的故事在今天可能也同樣重要。在比較中沒有一個算法性能特別差。

          BERT和USE的比賽千鈞一發。USE把故事繞到了社會問題,BERT關注浪漫和約會。其他算法則轉向了家庭和孩子的話題,可能是因為看到了“ex husband 前夫”這個詞。

          2. A Deep-Sea Magma Monster Gets a Body Scan

          TF-IDF獲勝。

          這篇科學文章是關于海洋中活火山的三維掃描。

          3D掃描、火山和海洋是罕見的術語。所有算法都很好地實現了公平。

          TF-IDF正確地選擇了那些只談論地球海洋內火山的人。USE與它相比也是一個強大的競爭者,它的重點是火星上的火山而不是海洋。另一些算法則選擇了有關俄羅斯軍用潛艇的文章,這些文章與科學無關,與主題無關。

          3. Renault and Nissan Try a New Way After Years When Carlos Ghosn Ruled

          TF-IDF獲勝。

          文章談到了前首席執行官卡洛斯·戈恩越獄后雷諾和日產的遭遇。

          理想的匹配將討論這3個實體。與前兩篇相比,本文更具有事件驅動性和時間敏感性。相關新聞應與此日期或之后發生(從2019年11月開始)。

          TF-IDF正確地選擇了關注日產CEO的文章。其他人則選擇了一些談論通用汽車行業新聞的文章,比如菲亞特克萊斯勒(Fiat Chrysler)和標致(Peugeot)的結盟。

          值得一提的是,Doc2vec和USE生成了完全相同的結果。

          4. Dominic Thiem Beats Rafael Nadal in Australian Open Quarterfinal

          Jaccard、TF-IDF和USE結果相似。

          這篇文章是關于網球選手多米尼克·蒂姆在2020年澳大利亞網球公開賽(網球比賽)上的文章。

          新聞是事件驅動的,對個人來說非常具體。所以理想的匹配是多米尼克和澳大利亞公開賽。

          不幸的是,這個結果由于缺乏足夠的數據而受到影響。他們都談論網球。但有些比賽是在談論2018年法國網球公開賽的多米尼克。或者,在澳大利亞網球公開賽上對費德勒的看法。

          結果是三種算法的結果。這說明了關鍵的重要性:我們需要盡最大努力收集、多樣化和擴展數據池,以獲得最佳的相似性匹配結果。

          5. 2020 Democrats Seek Voters in an Unusual Spot: Fox News

          USE勝利。

          這篇文章是關于民主黨人的,特別關注伯尼·桑德斯在福克斯新聞(Fox News)上為2020年大選出鏡。

          每一個話題都有自己的大問題。關于民主黨候選人和選舉的文章很多。因為這個故事的主旨是新穎的,所以我們優先討論民主黨候選人和福克斯的關系。

          旁注:在實踐中,你要小心對待政治上的建議。把自由和保守的新聞混合在一起很容易讓讀者不安。既然我們是單獨和《紐約時報》打交道,那就不必擔心了。

          USE找到了一些關于伯尼·桑德斯和福克斯、微軟全國廣播公司等電視頻道的文章。其他人則選擇了一些討論2020年大選中其他民主黨候選人的文章。

          速度之王

          在結束贏家之前,我們需要談談運行時間。每種算法在速度方面表現得非常不同。

          結果是,TF-IDF的實施比任何其他方法都快得多。要在單個CPU上從頭到尾計算33914個文檔(標識化、向量化和比較),需要:

          • TF-IDF:1.5分鐘。
          • Jaccard:13分鐘。
          • Doc2vec:43分鐘。
          • USE:62分鐘。
          • BERT:50多小時(每個句子都被向量化了)。

          TF-IDF只花了一分半鐘。這是USE的2.5%。當然,你可以合并多種效率增強。但潛在收益需要討論。這將使我們有另一個理由認真審視相關的利弊權衡。

          以下是5篇文章中的每一篇的贏家算法。

          1. BERT
          2. TF-IDF
          3. TF-IDF
          4. Jaccard, TF-IDF和USE
          5. USE

          從結果可以看出,對于新聞報道中的文檔相似性,TF-IDF是最佳候選。如果你使用它的最小定制,這一點尤其正確。考慮到TF-IDF是發明的第二古老的算法,這也令人驚訝。相反,你可能會失望的是,現代先進的人工智能深度學習在這項任務中沒有任何意義。

          當然,每種深度學習技術都可以通過訓練自己的模型和更好地預處理數據來改進。但所有這些都伴隨著開發成本。你想好好想想,相對于TF-IDF方法,這種努力會帶來額外多大的好處。

          最后,可以說我們應該完全忘記Jaccard和Doc2vec的文檔相似性。與今天的替代品相比,它們沒有帶來任何好處。

          新手推薦

          假設你決定從頭開始在應用程序中實現相似性算法,下面是我的建議。

          1.先實施TF-IDF

          最快的文檔相似性匹配是TF-IDF,盡管有深度學習的各種宣傳,例如深度學習給你一個高質量的結果。但是TFIDF最棒的是,它是閃電般的快。

          正如我們所看到的,將其升級到深度學習方法可能會或不會給你帶來更好的性能。在計算權衡時,必須事先考慮很多問題。

          2.積累更好的數據

          Andrew Ng給出了一個類似的建議。你不能指望你的車沒有油就跑。油必須是好的。

          文檔相似性依賴于數據的多樣性,也依賴于特定的算法。你應該盡你最大的努力找到唯一的數據來增強你的相似性結果。

          3.升級到深度學習

          僅當你對TF-IDF的結果不滿意時,才遷移到USE或BERT以升級模型。你需要考慮計算時間。你可能會預處理詞嵌入,因此你可以在運行時更快地處理相似性匹配。谷歌為此寫了一篇教程:https://cloud.google.com/solutions/machine-learning/building-real-time-embeddings-similarity-matching-system

          4.調整深度學習算法

          你可以慢慢升級你的模型。訓練你自己的模型,將預訓練好的知識融入特定的領域,等等。今天也有許多不同的深度學習模式。你可以一個一個的來看看哪一個最適合你的具體要求。

          文檔相似性是許多NLP任務之一

          你可以使用各種算法實現文檔的相似性:一些是傳統的統計方法,另一些是尖端的深度學習方法。我們已經在紐約時報的文章中看到了它們之間的比較。

          使用TF-IDF,你可以在本地筆記本電腦上輕松啟動自己的文檔相似性。不需要昂貴的GPU。不需要大內存。你仍然可以得到高質量的數據。

          誠然,如果你想做情緒分析或分類等其他任務,深入學習應該適合你的工作。但是,當研究人員試圖突破深度學習效率和成績界限時,我們要意識到生活在炒作的圈子里是不健康的。它給新來的人帶來巨大的焦慮和不安全感。

          本篇文章主要介紹了前端HTML5幾種存儲方式的總結 ,主要包括本地存儲localstorage,本地存儲sessionstorage,離線緩存(application cache),Web SQL,IndexedDB。有興趣的可以了解一下。

          正文開始~

          總體情況

          h5之前,存儲主要是用cookies。cookies缺點有在請求頭上帶著數據,大小是4k之內。主Domain污染。

          主要應用:購物車、客戶登錄

          對于IE瀏覽器有UserData,大小是64k,只有IE瀏覽器支持。

          目標

          1. 解決4k的大小問題
          2. 解決請求頭常帶存儲信息的問題
          3. 解決關系型存儲的問題
          4. 跨瀏覽器

          1.本地存儲localstorage

          存儲方式:

          以鍵值對(Key-Value)的方式存儲,永久存儲,永不失效,除非手動刪除。

          大小:

          每個域名5M

          支持情況:

          注意:IE9 localStorage不支持本地文件,需要將項目署到服務器,才可以支持!

          if(window.localStorage){
           
           alert('This browser supports localStorage');
           
          }else{
           
           alert('This browser does NOT support localStorage');
           
          } 
          

          常用的API:

          getItem //取記錄

          setIten//設置記錄

          removeItem//移除記錄

          key//取key所對應的值

          clear//清除記錄

          存儲的內容:

          數組,圖片,json,樣式,腳本。。。(只要是能序列化成字符串的內容都可以存儲)

          2.本地存儲sessionstorage

          HTML5 的本地存儲 API 中的 localStorage 與 sessionStorage 在使用方法上是相同的,區別在于 sessionStorage 在關閉頁面后即被清空,而 localStorage 則會一直保存。

          3.離線緩存(application cache)

          本地緩存應用所需的文件

          使用方法:

          ①配置manifest文件

          頁面上:

          <!DOCTYPE HTML>
           
          <html manifest="demo.appcache">
           
          ...
           
          </html> 
          

          Manifest 文件:

          manifest 文件是簡單的文本文件,它告知瀏覽器被緩存的內容(以及不緩存的內容)。

          manifest 文件可分為三個部分:

          ①CACHE MANIFEST - 在此標題下列出的文件將在首次下載后進行緩存

          ②NETWORK - 在此標題下列出的文件需要與服務器的連接,且不會被緩存

          ③FALLBACK - 在此標題下列出的文件規定當頁面無法訪問時的回退頁面(比如 404 頁面)

          完整demo:

          CACHE MANIFEST
           
          # 2016-07-24 v1.0.0
           
          /theme.css
           
          /main.js
           
           
           
          NETWORK:
           
          login.jsp
           
           
           
          FALLBACK:
           
          /html/ /offline.html 
          

          服務器上:manifest文件需要配置正確的MIME-type,即 "text/cache-manifest"。

          如Tomcat:

          <mime-mapping>
           
           <extension>manifest</extension>
           
           <mime-type>text/cache-manifest</mime-type>
           
          </mime-mapping> 
          

          常用API:

          核心是applicationCache對象,有個status屬性,表示應用緩存的當前狀態:

          0(UNCACHED) : 無緩存, 即沒有與頁面相關的應用緩存

          1(IDLE) : 閑置,即應用緩存未得到更新

          2 (CHECKING) : 檢查中,即正在下載描述文件并檢查更新

          3 (DOWNLOADING) : 下載中,即應用緩存正在下載描述文件中指定的資源

          4 (UPDATEREADY) : 更新完成,所有資源都已下載完畢

          5 (IDLE) : 廢棄,即應用緩存的描述文件已經不存在了,因此頁面無法再訪問應用緩存

          相關的事件:

          表示應用緩存狀態的改變:

          checking : 在瀏覽器為應用緩存查找更新時觸發

          error : 在檢查更新或下載資源期間發送錯誤時觸發

          noupdate : 在檢查描述文件發現文件無變化時觸發

          downloading : 在開始下載應用緩存資源時觸發

          progress:在文件下載應用緩存的過程中持續不斷地下載地觸發

          updateready : 在頁面新的應用緩存下載完畢觸發

          cached : 在應用緩存完整可用時觸發

          Application Cache的三個優勢:

          ① 離線瀏覽

          ② 提升頁面載入速度

          ③ 降低服務器壓力

          注意事項:

          1. 瀏覽器對緩存數據的容量限制可能不太一樣(某些瀏覽器設置的限制是每個站點 5MB)

          2. 如果manifest文件,或者內部列舉的某一個文件不能正常下載,整個更新過程將視為失敗,瀏覽器繼續全部使用老的緩存

          3. 引用manifest的html必須與manifest文件同源,在同一個域下

          4. 瀏覽器會自動緩存引用manifest文件的HTML文件,這就導致如果改了HTML內容,也需要更新版本才能做到更新。

          5. manifest文件中CACHE則與NETWORK,FALLBACK的位置順序沒有關系,如果是隱式聲明需要在最前面

          6. FALLBACK中的資源必須和manifest文件同源

          7. 更新完版本后,必須刷新一次才會啟動新版本(會出現重刷一次頁面的情況),需要添加監聽版本事件。

          8. 站點中的其他頁面即使沒有設置manifest屬性,請求的資源如果在緩存中也從緩存中訪問

          9. 當manifest文件發生改變時,資源請求本身也會觸發更新

          離線緩存與傳統瀏覽器緩存區別:

          1. 離線緩存是針對整個應用,瀏覽器緩存是單個文件

          2. 離線緩存斷網了還是可以打開頁面,瀏覽器緩存不行

          3. 離線緩存可以主動通知瀏覽器更新資源

          4.Web SQL

          關系數據庫,通過SQL語句訪問

          Web SQL 數據庫 API 并不是 HTML5 規范的一部分,但是它是一個獨立的規范,引入了一組使用 SQL 操作客戶端數據庫的 APIs。

          支持情況:

          Web SQL 數據庫可以在最新版的 Safari, Chrome 和 Opera 瀏覽器中工作。

          核心方法:

          ①openDatabase:這個方法使用現有的數據庫或者新建的數據庫創建一個數據庫對象。

          ②transaction:這個方法讓我們能夠控制一個事務,以及基于這種情況執行提交或者回滾。

          ③executeSql:這個方法用于執行實際的 SQL 查詢。

          打開數據庫:

          var db=openDatabase('mydb', '1.0', 'Test DB', 2 * 1024 * 1024,fn);
           
          //openDatabase() 方法對應的五個參數分別為:數據庫名稱、版本號、描述文本、數據庫大小、創建回調
          

          執行查詢操作:

          var db=openDatabase('mydb', '1.0', 'Test DB', 2 * 1024 * 1024);
           
          db.transaction(function (tx) { 
           
           tx.executeSql('CREATE TABLE IF NOT EXISTS WIN (id unique, name)');
           
          }); 
          

          插入數據: 

          var db=openDatabase('mydb', '1.0', 'Test DB', 2 * 1024 * 1024);
           
          db.transaction(function (tx) {
           
           tx.executeSql('CREATE TABLE IF NOT EXISTS WIN (id unique, name)');
           
           tx.executeSql('INSERT INTO WIN (id, name) VALUES (1, "winty")');
           
           tx.executeSql('INSERT INTO WIN (id, name) VALUES (2, "LuckyWinty")');
           
          }); 
          

          讀取數據:

          db.transaction(function (tx) {
           
           tx.executeSql('SELECT * FROM WIN', [], function (tx, results) {
           
           var len=results.rows.length, i;
           
           msg="<p>查詢記錄條數: " + len + "</p>";
           
           document.querySelector('#status').innerHTML +=msg; 
           
           for (i=0; i < len; i++){
           
           alert(results.rows.item(i).name );
           
           }
           
           }, null);
           
          }); 
          

          由這些操作可以看出,基本上都是用SQL語句進行數據庫的相關操作,如果你會MySQL的話,這個應該比較容易用。

          5.IndexedDB

          索引數據庫 (IndexedDB) API(作為 HTML5 的一部分)對創建具有豐富本地存儲數據的數據密集型的離線 HTML5 Web 應用程序很有用。同時它還有助于本地緩存數據,使傳統在線 Web 應用程序(比如移動 Web 應用程序)能夠更快地運行和響應。

          異步API:

          在IndexedDB大部分操作并不是我們常用的調用方法,返回結果的模式,而是請求——響應的模式,比如打開數據庫的操作

          這樣,我們打開數據庫的時候,實質上返回了一個DB對象,而這個對象就在result中。由上圖可以看出,除了result之外。還有幾個重要的屬性就是onerror、onsuccess、onupgradeneeded(我們請求打開的數據庫的版本號和已經存在的數據庫版本號不一致的時候調用)。這就類似于我們的ajax請求那樣。我們發起了這個請求之后并不能確定它什么時候才請求成功,所以需要在回調中處理一些邏輯。

          關閉與刪除:

          function closeDB(db){
           
           db.close();
           
          }
           
          function deleteDB(name){
           
           indexedDB.deleteDatabase(name);
           
          } 
          

          數據存儲:

          indexedDB中沒有表的概念,而是objectStore,一個數據庫中可以包含多個objectStore,objectStore是一個靈活的數據結構,可以存放多種類型數據。也就是說一個objectStore相當于一張表,里面存儲的每條數據和一個鍵相關聯。

          我們可以使用每條記錄中的某個指定字段作為鍵值(keyPath),也可以使用自動生成的遞增數字作為鍵值(keyGenerator),也可以不指定。選擇鍵的類型不同,objectStore可以存儲的數據結構也有差異。

          學習從來不是一個人的事情,要有個相互監督的伙伴,想要學習或交流前端問題的小伙伴可以私信“學習”小明獲取web前端入門資料,一起學習,一起成長!

          基本概念下手,讓我們來創建一個簡單的表,并在表中創建幾條記錄。


          創建測試數據

          接下來我們在 MySQL 中創建 RUNOOB 數據庫,并創建 websites 數據表,表結構如下:

          CREATE TABLE `websites` (
           `id` int(11) NOT NULL AUTO_INCREMENT,
           `name` char(20) NOT NULL DEFAULT '' COMMENT '站點名稱',
           `url` varchar(255) NOT NULL DEFAULT '',
           `alexa` int(11) NOT NULL DEFAULT '0' COMMENT 'Alexa 排名',
           `country` char(10) NOT NULL DEFAULT '' COMMENT '國家',
           PRIMARY KEY (`id`)) ENGINE=InnoDB AUTO_INCREMENT=10 DEFAULT CHARSET=utf8;

          插入一些數據:

          INSERT INTO `websites` VALUES ('1', 'Google', 'https://www.google.cm/', '1', 'USA'), ('2', '淘寶', 'https://www.taobao.com/', '13', 'CN'), ('3', '菜鳥教程', 'http://www.runoob.com', '5892', ''), ('4', '微博', 'http://weibo.com/', '20', 'CN'), ('5', 'Facebook', 'https://www.facebook.com/', '3', 'USA');



          訪問數據庫

          下面的實例演示了如何使用 Servlet 訪問 RUNOOB 數據庫。

          package com.runoob.test;import java.io.IOException;import java.io.PrintWriter;import java.sql.*;import javax.servlet.ServletException;import javax.servlet.annotation.WebServlet;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;/**
          * Servlet implementation class DatabaseAccess
          */@WebServlet("/DatabaseAccess")public class DatabaseAccess extends HttpServlet { private static final long serialVersionUID=1L; // JDBC 驅動名及數據庫 URL static final String JDBC_DRIVER="com.mysql.jdbc.Driver"; static final String DB_URL="jdbc:mysql://localhost:3306/RUNOOB"; 
           // 數據庫的用戶名與密碼,需要根據自己的設置 static final String USER="root"; static final String PASS="123456";
           /**
           * @see HttpServlet#HttpServlet()
           */
           public DatabaseAccess() {
           super();
           // TODO Auto-generated constructor stub
           } /**
           * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
           */ protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { Connection conn=null; Statement stmt=null; // 設置響應內容類型 response.setContentType("text/html;charset=UTF-8"); PrintWriter out=response.getWriter(); String title="Servlet Mysql 測試 - 菜鳥教程"; String docType="<!DOCTYPE html>\n"; out.println(docType + "<html>\n" + "<head><title>" + title + "</title></head>\n" + "<body bgcolor=\"#f0f0f0\">\n" + "<h1 align=\"center\">" + title + "</h1>\n"); try{ // 注冊 JDBC 驅動器 Class.forName("com.mysql.jdbc.Driver"); 
           // 打開一個連接 conn=DriverManager.getConnection(DB_URL,USER,PASS); // 執行 SQL 查詢 stmt=conn.createStatement(); String sql; sql="SELECT id, name, url FROM websites"; ResultSet rs=stmt.executeQuery(sql); // 展開結果集數據庫 while(rs.next()){ // 通過字段檢索 int id=rs.getInt("id"); String name=rs.getString("name"); String url=rs.getString("url"); 
           // 輸出數據 out.println("ID: " + id); out.println(", 站點名稱: " + name); out.println(", 站點 URL: " + url); out.println("<br />"); } out.println("</body></html>"); // 完成后關閉 rs.close(); stmt.close(); conn.close(); } catch(SQLException se) { // 處理 JDBC 錯誤 se.printStackTrace(); } catch(Exception e) { // 處理 Class.forName 錯誤 e.printStackTrace(); }finally{ // 最后是用于關閉資源的塊 try{ if(stmt!=null) stmt.close(); }catch(SQLException se2){ } try{ if(conn!=null) conn.close(); }catch(SQLException se){ se.printStackTrace(); } } 
           } /**
           * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
           */ protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // TODO Auto-generated method stub doGet(request, response); }}

          現在讓我們來編譯上面的 Servlet,并在 web.xml 文件中創建以下條目:

          .... <servlet> <servlet-name>DatabaseAccess</servlet-name> <servlet-class>com.runoob.test.DatabaseAccess</servlet-class> </servlet> <servlet-mapping> <servlet-name>DatabaseAccess</servlet-name> <url-pattern>/TomcatTest/DatabaseAccess</url-pattern> </servlet-mapping>....

          主站蜘蛛池模板: 日本精品高清一区二区| 精品欧洲av无码一区二区| 一区二区三区91| 亚洲日韩国产精品第一页一区| 日韩精品一区二区三区中文版 | 无码人妻一区二区三区一| 国产精品无码一区二区在线观一 | 国产成人一区二区三区免费视频| 狠狠色婷婷久久一区二区| 国产午夜精品一区二区三区极品| 天堂资源中文最新版在线一区 | 日韩高清一区二区三区不卡 | www一区二区www免费| 国产午夜精品一区二区三区| 亚洲一区无码中文字幕| 在线观看国产区亚洲一区成人 | 夜夜嗨AV一区二区三区| 3D动漫精品啪啪一区二区下载| 一区二区三区无码高清视频| 国产精品一区二区在线观看| 精品国产免费一区二区| 国精产品一区一区三区有限在线| 色噜噜狠狠一区二区三区果冻| 无码人妻精品一区二区三区99仓本| 国产精品一区二区在线观看| 日韩在线视频一区二区三区 | 日本免费一区二区三区最新vr| 日本一区二区在线播放| 久久er99热精品一区二区 | 人妻无码久久一区二区三区免费 | 久久精品一区二区国产| 成人国内精品久久久久一区| 日韩综合无码一区二区| 日韩人妻无码一区二区三区 | 精品国产一区二区三区av片| 日韩aⅴ人妻无码一区二区| 日韩精品一区二区三区影院| 亚洲成人一区二区| 岛国无码av不卡一区二区| 麻豆精品久久久一区二区| 亚洲一区免费视频|