N-gram verilerinin depolanması


12

N -gram verilerinin saklanması konusunda biraz beyin fırtınası yapmayı umuyordum . Projemde, ben bütün (biliyorum dilsel sorunları çözmek için çalışıyorum n -1) veri öğeleri ve istatistiki benim tahmin etmek ister n geçerli tüm üzerinde lineer interpolasyon kullanarak n -grams. (Evet, sözlüğüne göre bilinen kelimelere etiketler atayan bir etiketleyici ve bilinmeyen kelimeler için tür kelimesini tahmin etmeye çalışan bir sonek ağacı vardır; burada tartışılan n -gram bileşeni, ambuguity'yi çözmekle görevlendirilecektir.)

İlk yaklaşımım, gözlemlenen tüm n -gramlarını ( n = 1..3 için, yani monogram, bigram, trigram) verileri ilgili SQL veritabanlarında depolamak ve bir gün olarak adlandırmak olacaktır. Ancak projemin gereksinimleri diğer vektör uzunluklarını ( n ) içerecek şekilde değişebilir ve uygulamamın çok fazla iş yapmadan 4 grama uyum sağlamasını istiyorum (şemayı güncelleme, uygulama kodunu güncelleme vb.); ideal olarak, basitçe kodumu çok fazla (veya hiç) değiştirmek ve verilerini belirli bir veri kaynağından eğitmek zorunda kalmadan uygulamama şimdi 4 gram ile çalışmasını söylerdim.

Tüm gereksinimleri özetlemek için:

  • N -gram verilerini depolayabilme (başlangıçta n = {1, 2, 3} için)
  • Ne tür n- gramların kullanılması gerektiğini değiştirme yeteneği (uygulama çalışmaları arasında)
  • N -gram verilerini (uygulama çalıştırmaları arasında ) eğitme (yeniden- )
  • Veri deposunu sorgulama yeteneği (örneğin, A, B, C'yi gözlemlediysem, eğitimli 4-, 3-, 2-, 1-gram veri setlerimi kullanarak takip edebilecekler için en sık gözlenen öğeyi bilmek istiyorum )

    Uygulama büyük olasılıkla okuma-ağır olacak, veri setleri büyük olasılıkla sık sık yeniden eğitilmeyecek

  • Çözüm .NET Framework'ü kullanıyor (4.0'a kadar)

Şimdi böyle bir görev için hangi tasarım daha uygun olurdu?

  • Her n için bir SQL sunucusu (MSSQL, MySQL, ...) tarafından yönetilen sabit bir tablo (örneğin, iki gram, üç gram vb. İçin ayrılmış tablolar)
  • Veya ilk n -1'i belgenin anahtarı olarak saklayan ve belgenin kendisi n- değerini ve gözlemlenen frekansları içeren bir NoSQL belge veritabanı çözümü ?
  • Yoksa farklı bir şey mi?

3
Bunun Stack Overflow için daha uygun olacağını düşünüyorum.
Konrad Rudolph

1
Belki bir trie (önek ağacı) veri yapısı gereksinimlerinize uygun olabilir?
Zamanlayıcı

1
Stack Overflow veya hatta cstheory.stackexchange.com öneririm
Steve

Tamam teşekkürler. Soruyu oraya getirmeye çalışacağım.
Manny

4
Bu soru programmers.stackexchange.com için çok uygundur ve stackoverflow, IMO'ya taşınmamalıdır. Tam olarak burada sorulması gereken bir tür “beyaz tahta durumu” sorusu. Ayrıntılar için meta'ya bakın.
user281377

Yanıtlar:


8

En uygun N aralığını bilmediğiniz göz önüne alındığında, kesinlikle onu değiştirmek istersiniz. Örneğin, uygulamanız belirli bir metnin İngilizce olduğunu tahmin ederse, muhtemelen N 3..5 için N-gram karakter kullanmak istersiniz. (Deneysel olarak bulduğumuz şey budur.)

Başvurunuzla ilgili ayrıntıları paylaşmadınız, ancak sorun yeterince açık. İlişkisel bir veritabanında (veya NoSQL belge tabanlı çözümde) N-gram verilerini temsil etmek istiyorsunuz. Kendi çözümümü önermeden önce, aşağıdaki yaklaşımlara göz atmak isteyebilirsiniz:

  1. Google ngramlarını bir veritabanında en iyi şekilde nasıl saklayabilirim?
  2. N-gramın veritabanında <n tablo sayısı içinde depolanması
  3. İlişkisel Veritabanı ile Google Web 1T 5 gram'ı yönetme

Şimdi, yukarıdaki bağlantılardan herhangi birini okumadıktan sonra, N-gramın her boyutu için bir tane olmak üzere birden çok tablo kullanarak basit, ilişkisel bir veritabanı yaklaşımı öneririm. Tüm verileri maksimum gerekli sütunlarla tek bir tabloya koyabilirsiniz (yani, son sütunları boş bırakarak, ngram_4'te bigram ve trigram depolayın), ancak verileri bölümlendirmenizi öneririz. Veritabanı motorunuza bağlı olarak, çok sayıda satırı olan tek bir tablo performansı olumsuz etkileyebilir.

  create table ngram_1 (
      word1 nvarchar(50),
      frequency FLOAT,
   primary key (word1));

  create table ngram_2 (
      word1 nvarchar(50),
      word2 nvarchar(50),
      frequency FLOAT,
   primary key (word1, word2));

  create table ngram_3 (
      word1 nvarchar(50),
      word2 nvarchar(50),
      word3 nvarchar(50),
      frequency FLOAT,
   primary key (word1, word2, word3));

  create table ngram_4 (
      word1 nvarchar(50),
      word2 nvarchar(50),
      word3 nvarchar(50),
      word4 nvarchar(50),
      frequency FLOAT,
   primary key (word1, word2, word3, word4));

Daha sonra, size tüm ngram tablolarınız verildiğinde en olası sonraki kelimeyi döndürecek bir sorgu vereceğim. Ama önce, yukarıdaki tablolara eklemeniz gereken bazı örnek veriler:

  INSERT [ngram_2] ([word1], [word2], [frequency]) VALUES (N'building', N'with', 0.5)
  INSERT [ngram_2] ([word1], [word2], [frequency]) VALUES (N'hit', N'the', 0.1)
  INSERT [ngram_2] ([word1], [word2], [frequency]) VALUES (N'man', N'hit', 0.2)
  INSERT [ngram_2] ([word1], [word2], [frequency]) VALUES (N'the', N'bat', 0.7)
  INSERT [ngram_2] ([word1], [word2], [frequency]) VALUES (N'the', N'building', 0.3)
  INSERT [ngram_2] ([word1], [word2], [frequency]) VALUES (N'the', N'man', 0.4)
  INSERT [ngram_2] ([word1], [word2], [frequency]) VALUES (N'with', N'the', 0.6)
  INSERT [ngram_3] ([word1], [word2], [word3], [frequency]) VALUES (N'building', N'with', N'the', 0.5)
  INSERT [ngram_3] ([word1], [word2], [word3], [frequency]) VALUES (N'hit', N'the', N'building', 0.3)
  INSERT [ngram_3] ([word1], [word2], [word3], [frequency]) VALUES (N'man', N'hit', N'the', 0.2)
  INSERT [ngram_3] ([word1], [word2], [word3], [frequency]) VALUES (N'the', N'building', N'with', 0.4)
  INSERT [ngram_3] ([word1], [word2], [word3], [frequency]) VALUES (N'the', N'man', N'hit', 0.1)
  INSERT [ngram_3] ([word1], [word2], [word3], [frequency]) VALUES (N'with', N'the', N'bat', 0.6)
  INSERT [ngram_4] ([word1], [word2], [word3], [word4], [frequency]) VALUES (N'building', N'with', N'the', N'bat', 0.5)
  INSERT [ngram_4] ([word1], [word2], [word3], [word4], [frequency]) VALUES (N'hit', N'the', N'building', N'with', 0.3)
  INSERT [ngram_4] ([word1], [word2], [word3], [word4], [frequency]) VALUES (N'man', N'hit', N'the', N'building', 0.2)
  INSERT [ngram_4] ([word1], [word2], [word3], [word4], [frequency]) VALUES (N'the', N'building', N'with', N'the', 0.4)
  INSERT [ngram_4] ([word1], [word2], [word3], [word4], [frequency]) VALUES (N'the', N'man', N'hit', N'the', 0.1)

En olası sonraki kelimeyi sorgulamak için böyle bir sorgu kullanırsınız.

  DECLARE @word1 NVARCHAR(50) = 'the'
  DECLARE @word2 NVARCHAR(50) = 'man'
  DECLARE @word3 NVARCHAR(50) = 'hit'
  DECLARE @bigramWeight FLOAT = 0.2;
  DECLARE @trigramWeight FLOAT = 0.3
  DECLARE @fourgramWeight FLOAT = 0.5

  SELECT next_word, SUM(frequency) AS frequency
  FROM (
    SELECT word2 AS next_word, frequency * @bigramWeight AS frequency
    FROM ngram_2
    WHERE word1 = @word3
    UNION
    SELECT word3 AS next_word, frequency * @trigramWeight AS frequency
    FROM ngram_3
    WHERE word1 = @word2
      AND word2 = @word3
    UNION
    SELECT word4 AS next_word, frequency * @fourgramWeight AS frequency
    FROM ngram_4
    WHERE word1 = @word1
      AND word2 = @word2
      AND word3 = @word3
    ) next_words
  GROUP BY next_word
  ORDER BY SUM(frequency) DESC

Daha fazla ngram tablosu eklerseniz, yukarıdaki sorguya başka bir UNION deyimi eklemeniz gerekir. İlk sorguda word1 = @ word3 kullandığımı fark edebilirsiniz. Ve ikinci sorguda, word1 = @ word2 VE word2 = @ word3. Çünkü ngram verileri için sorgudaki üç kelimeyi hizalamamız gerekiyor . Üç kelimelik bir dizi için en olası sonraki kelimeyi istiyorsak, bigram verilerindeki ilk kelimeyi dizideki kelimelerin son kelimesine karşı kontrol etmemiz gerekir .

Ağırlık parametrelerini dilediğiniz gibi değiştirebilirsiniz. Bu örnekte, daha yüksek sıra sayıdaki "n" gramın daha güvenilir olacağını varsaydım.

PS Ben program kodu yapılandırma üzerinden herhangi bir sayıda ngram_N tablolarını işlemek için yapı. Programı, ngram_5 ve ngram_6 tablolarını oluşturduktan sonra N-gram aralığı N (1..6) kullanacak şekilde bildirimsel olarak değiştirebilirsiniz.


Bu sorgu ile, sadece burada var frekans puanını görüyorum. Bir sonraki tahmini kelimeyi nasıl seçerim. Cümle ile en alakalı olan hangisi?
TomSawyer

İyi nokta @TomSawyer. Cevaba örnek veri ekledim ve en olası sonraki kelimeyi döndüren örnek bir sorgu verdim.
Matthew Rodatus

Güncellemeniz için teşekkürler. Fakat burada frekansı nasıl hesaplayabiliriz? yani: içinde ngram_2, cümle building withfrekansı 0.5'dir. Aynı soru @bigramWeight, bu nedir? Ben freq alan her veritabanı güncellemek güncelleme olacak olsa. Yani kullanıcı daha fazla dize girerse, bu dize için sıklık yeniden hesaplanacaktır? 0,5 her bir ifadenin toplam kullanım süresinde veya görünüm oranında yüzde 0,5 mi?
TomSawyer

BigramWeight ve trigramWeight (vb.), Genel hesaplamada farklı n-gramların nasıl ağırlıklandırılacağıdır. Daha uzun n-gramların daha yüksek entropiye sahip olduğunu söylemenin basit bir yolu ve daha kısa n-gramdan daha fazla "saymalarını" isteyebilirsiniz.
Matthew Rodatus

Veritabanını güncelleme açısından, tüm detayları kapsamadım ve iyileştirilmesi gereken çok şey var. Örneğin, nvarchars'ı ngram tablolarında depolamak yerine, muhtemelen bir kelime tablosuna (word_id INT, word NVARCHAR) tokenleştirmek ve ngram tablolarındaki word_ids'e başvurmak istersiniz. Yeniden eğitim ile ilgili tabloları güncellemek için bu doğru - sadece frekans alanını güncellersiniz.
Matthew Rodatus

3

Diğerlerinin önerdiklerinin aksine, bir hashmap veya anahtar / değer deposundan daha karmaşık veri yapılarından kaçınmayı öneririm.

Veri erişim gereksinimlerinizi aklınızda bulundurun: a)% 99 istek - "aaa-bbb-ccc" sorgu ngramı ve değeri (veya 0) geri alma b)% 1 istek - belirli bir ngram sayısını ekleme / güncelleme c) yok (c).

En etkili yol, tek bir arama ile geri getirmektir. Tam n-gram'ı tek bir dizede (örn. 3gram için "alpha | beta | gamma", unigram için "alpha" vb.) Birleştirmek için sınırların dışında (veya çıkış karakterli) bir ayırıcı kullanabilirsiniz ve ( bunun karmasıyla). Çok sayıda NLP yazılımı bunu yapar.

Ngram verileriniz küçükse (örneğin <1 gb) ve belleğe sığarsa, ek yükü önlemek için etkili bir program içi bellek yapısı (hashmaps, ağaçlar, denemeler vb.) Kullanmanızı öneririm; ve sadece düz dosyalara serileştirin / serisini kaldırın. Ngram verileriniz terabayt veya daha fazlaysa, birden çok düğüme bölünmüş NoSQL anahtar / değer depolarını seçebilirsiniz.

Ekstra performans için, her yerde tüm kelimeleri tamsayı kimlikleriyle değiştirmek isteyebilirsiniz, böylece çekirdek algoritmanız hiç (yavaş) dizeyi görmez; aynı fikri uygulamak biraz farklıdır.


1

En verimli değil, basit ve istediğiniz gibi veritabanına evlenmiş:

Table: word
Colums:
word (int, primary key) - a unique identifier for each word
text (varchar) - the actual word

Table: wordpos
Columns:
document (int) - a unique identified for the document of this word
word (int, foreign key to word.word) - the word in this position
pos (int) - the position of this word (e.g., first word is 1, next is 2, ...)

wordpos'un belge ve konum üzerinde dizinleri olmalıdır.

bigramlar:

select word1.text as word1, word2.text as word2
from wordpos as pos1, wordpos as pos2, word as word1, word as word2
where pos1.document = pos2.document
      and pos1.pos = pos2.pos - 1
      and word1.word = pos1.word
      and word2.word = pos2.word

Sonra frekansları ve şeyleri sayabilir () ve gruplayabilirsiniz.

Üçgene geçmek için bu dizeyi bir word3 içerecek şekilde oluşturmak kolaydır.

Aslında daha önce yaptım (orada SQL muhtemelen biraz paslı olmasına rağmen). Kolayca diskten çıkarılabilecek bir dizi düz dosyaya yerleştim. Kinda donanımınıza nasıl daha iyi yapılacağına bağlıdır.


1

Uygulamalarımın unigramlardan bigram ve trigramlara yönelik basit aramalarını geliştirmeye çalışırken, aslında sorunuzu gördüm.

Gereksinimlerden biri dağıtılmış bir dosya sistemini veya veritabanını sorgulama yeteneğine sahipse, bu sizin için de ilginç olabilir: Pibiri ve Venturini 2018 "Devasa N-Gram Veri Kümelerini Verimli Bir Şekilde Kullanma", n-gram verilerini depolamak için etkili bir yol çiziyor çalışma zamanı ve alan şartları. Uygulamalarını https://github.com/jermp/tongrams adresinde sundular.

Her n-gramlık "n", çok hızlı seçme ve sorgulama yeteneklerine sahip minimum mükemmel karma işleviyle erişilen ayrı bir tabloda tutulur. Tablolar statiktir ve Google n-gram metin dosyaları biçiminde girdi kullanılarak ana kod tarafından oluşturulur.

Kodu henüz kullanmadım, ancak sorgularınızın nereden geldiğine ilişkin açık gereksinimlerinizle yapabileceğiniz birçok yol var.

Bunun bir yolu: bir sunucu uygulamasının .NET eşdeğeri bir veritabanı veya veri deposuyla kullanılıyorsa ve depolama alanını korumanız gerekiyorsa, her ngram tablosunu bir tablo olarak veritabanında / veri deposunda ikili biçimde depolamak bir seçenektir (bir veritabanı / datastore tablosu, 1 gramın tümü için verimli ngram kodunun, diğeri 2 gramın tamamı için, vb. sonuçta elde edilen statik dosya için). Sorgular, verimli n-gram kodu (sunucu uygulamanız tarafından erişilebilir olması için sarılır) çağrılarak çalıştırılır. Bu, dağıtılmış bir dosya sistemindeki dosyalara erişmek için verimli n-gram kodunu kullanan dağıtılmış bir veritabanı yapmak için bir çözümdür. İkili veritabanı / veri deposu tablolarının her birinin temel dosya sisteminin dosya boyutu kısıtlamasına sahip olduğunu unutmayın.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.