Lưu trữ dữ liệu n-gram


12

Tôi đã hy vọng động não một chút về chủ đề lưu trữ dữ liệu n -gram. Trong dự án của tôi, tôi đang cố gắng giải quyết các vấn đề ngôn ngữ trong đó tôi biết tất cả ( n -1) mục dữ liệu và muốn thống kê đoán n của tôi bằng cách sử dụng phép nội suy tuyến tính trên tất cả các chương trình n áp dụng . (Có, có một trình gắn thẻ gán thẻ cho các từ đã biết theo từ vựng của nó và cây hậu tố cố gắng đoán loại từ cho các từ chưa biết; thành phần n -gram được thảo luận ở đây sẽ được giao nhiệm vụ giải quyết ambuguity.)

Cách tiếp cận ban đầu của tôi sẽ chỉ đơn giản là lưu trữ tất cả các chương trình n quan sát (đối với n = 1..3, tức là dữ liệu monogram, bigram, trigram) trong cơ sở dữ liệu SQL tương ứng và gọi nó là một ngày. Nhưng các yêu cầu của dự án của tôi có thể thay đổi để bao gồm các độ dài vectơ khác ( n ) và tôi muốn ứng dụng của mình thích ứng với 4 gram mà không cần nhiều công việc (cập nhật lược đồ, cập nhật mã ứng dụng, v.v.); lý tưởng, tôi chỉ đơn giản là bảo ứng dụng của tôi hoạt động với 4 gram ngay bây giờ mà không phải thay đổi mã nhiều (hoặc hoàn toàn) và huấn luyện dữ liệu của nó từ một nguồn dữ liệu nhất định.

Tổng hợp tất cả các yêu cầu:

  • Khả năng lưu trữ dữ liệu n -gram (ban đầu cho n = {1, 2, 3}
  • Khả năng thay đổi loại chương trình n nên được sử dụng (giữa các lần chạy ứng dụng)
  • Khả năng (tái) đào tạo dữ liệu n -gram (giữa các lần chạy ứng dụng)
  • Khả năng truy vấn kho lưu trữ dữ liệu (ví dụ: nếu tôi đã quan sát A, B, C, tôi muốn biết mục được quan sát thường xuyên nhất cho những gì có thể xảy ra bằng cách sử dụng các bộ dữ liệu 4, 3, 2, 1 gram được đào tạo của tôi )

    Ứng dụng rất có thể sẽ nặng về đọc, các bộ dữ liệu rất có thể sẽ không được đào tạo lại thường xuyên

  • Giải pháp sử dụng .NET Framework (tối đa 4.0)

Bây giờ thiết kế nào sẽ phù hợp hơn cho một nhiệm vụ như vậy?

  • Một bảng cố định được quản lý bởi máy chủ SQL (MSSQL, MySQL, ...) cho mỗi n (ví dụ: các bảng dành riêng cho bi-gram, tri-gram, v.v.)
  • Hoặc một giải pháp cơ sở dữ liệu tài liệu NoQuery lưu trữ n -1 đầu tiên làm khóa của tài liệu và bản thân tài liệu chứa giá trị thứ n và tần số quan sát được?
  • Hay một cái gì đó khác nhau?

3
Tôi nghĩ rằng điều này sẽ phù hợp hơn trên Stack Overflow.
Konrad Rudolph

1
Có lẽ một cấu trúc dữ liệu trie (cây tiền tố) sẽ phù hợp với yêu cầu của bạn?
SCHEDLER

1
Tôi muốn đề xuất Stack Overflow hoặc thậm chí cstheory.stackexchange.com
Steve

Được rồi, cảm ơn. Tôi sẽ cố gắng đưa câu hỏi lên đó.
Manny

4
Câu hỏi này hoàn toàn phù hợp với lập trình viên.stackexchange.com và không nên được chuyển sang stackoverflow, IMO. Đây chính xác là loại câu hỏi tình trạng bảng trắng của câu hỏi nên được hỏi ở đây. Kiểm tra meta để biết chi tiết.
user281377

Câu trả lời:


8

Cho rằng bạn sẽ không biết phạm vi tối ưu của N, bạn chắc chắn muốn có thể thay đổi nó. Ví dụ: nếu ứng dụng của bạn dự đoán khả năng một văn bản nhất định là tiếng Anh, có lẽ bạn sẽ muốn sử dụng ký tự N-gram cho N 3..5. (Đó là những gì chúng tôi tìm thấy bằng thực nghiệm.)

Bạn chưa chia sẻ chi tiết về ứng dụng của mình, nhưng vấn đề đã đủ rõ ràng. Bạn muốn biểu diễn dữ liệu N-gram trong cơ sở dữ liệu quan hệ (hoặc giải pháp dựa trên tài liệu NoQuery). Trước khi đề xuất một giải pháp của riêng tôi, bạn có thể muốn xem qua các phương pháp sau:

  1. Làm cách nào để lưu trữ Google ngrams tốt nhất trong cơ sở dữ liệu?
  2. Lưu trữ n-gram trong cơ sở dữ liệu trong <n số bảng
  3. Quản lý Google Web 1T 5 gram với Cơ sở dữ liệu quan hệ

Bây giờ, khi chưa đọc bất kỳ liên kết nào ở trên, tôi đề xuất một cách tiếp cận cơ sở dữ liệu quan hệ đơn giản bằng nhiều bảng, mỗi bảng cho mỗi kích thước của N-gram. Bạn có thể đặt tất cả dữ liệu vào một bảng với các cột cần thiết tối đa (nghĩa là lưu trữ bigram và trigram trong ngram_4, để các cột cuối cùng là null), nhưng tôi khuyên bạn nên phân vùng dữ liệu. Tùy thuộc vào công cụ cơ sở dữ liệu của bạn, một bảng có số lượng hàng lớn có thể ảnh hưởng tiêu cực đến hiệu suất.

  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));

Tiếp theo, tôi sẽ cung cấp cho bạn một truy vấn sẽ trả về từ tiếp theo có thể xảy ra nhất với tất cả các bảng ngram của bạn. Nhưng trước tiên, đây là một số dữ liệu mẫu mà bạn nên chèn vào các bảng trên:

  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)

Để truy vấn từ tiếp theo có thể xảy ra nhất, bạn sẽ sử dụng truy vấn như thế này.

  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

Nếu bạn thêm nhiều bảng ngram, bạn sẽ cần thêm một mệnh đề UNION khác cho truy vấn trên. Bạn có thể nhận thấy rằng trong truy vấn đầu tiên tôi đã sử dụng word1 = @ word3. Và trong truy vấn thứ hai, word1 = @ word2 AND word2 = @ word3. Đó là bởi vì chúng ta cần căn chỉnh ba từ trong truy vấn cho dữ liệu ngram. Nếu chúng ta muốn từ tiếp theo có khả năng nhất cho một chuỗi gồm ba từ, chúng ta sẽ cần kiểm tra từ đầu tiên trong dữ liệu bigram so với từ cuối cùng của các từ trong chuỗi.

Bạn có thể điều chỉnh các thông số trọng lượng như bạn muốn. Trong ví dụ này, tôi giả định rằng gram "n" gram cao hơn sẽ đáng tin cậy hơn.

PS Tôi sẽ cấu trúc mã chương trình để xử lý bất kỳ số lượng bảng ngram_N nào thông qua cấu hình. Bạn có thể thay đổi khai báo chương trình để sử dụng phạm vi N-gram N (1..6) sau khi tạo các bảng ngram_5 và ngram_6.


Với truy vấn này, tôi chỉ thấy điểm tần số bạn có ở đây. Làm thế nào để tôi chọn từ dự đoán tiếp theo. Đó là liên quan nhất đến câu?
TomSawyer

Điểm tốt @TomSawyer. Tôi đã thêm dữ liệu mẫu vào câu trả lời và đưa ra một truy vấn mẫu trả về từ tiếp theo có thể xảy ra nhất.
Matthew Rodatus

Tks cho cập nhật của bạn. Nhưng làm thế nào chúng ta có thể tính toán tần số ở đây? tức là: trong ngram_2, cụm từ building withcó freq là 0,5. Cùng một câu hỏi với @bigramWeight, đó là gì ?. Tôi mặc dù freq là trường sẽ được cập nhật mỗi khi chúng tôi cập nhật cơ sở dữ liệu. Tức là nếu người dùng nhập thêm chuỗi, tần số cho chuỗi này sẽ được tính toán lại? 0,5 là 0,5 phần trăm trong tổng số lần sử dụng hoặc tỷ lệ xuất hiện của mỗi cụm từ?
TomSawyer

Bigram Weight và trigram Weight (vv) là cách cân trọng lượng n-gram khác nhau trong tính toán tổng thể. Đó là một cách đơn giản để nói rằng n-gram dài hơn có entropy cao hơn và bạn có thể muốn chúng "đếm" nhiều hơn n-gram ngắn hơn.
Matthew Rodatus 4/12/2015

Về việc cập nhật cơ sở dữ liệu, rõ ràng tôi chưa bao gồm tất cả các chi tiết và có rất nhiều chỗ để cải thiện. Ví dụ, thay vì lưu trữ nvarchar trong các bảng ngram, có lẽ bạn muốn mã hóa thành một bảng từ (word_id INT, từ NVARCHAR) và sau đó tham khảo word_ids trong các bảng ngram. Để cập nhật các bảng về đào tạo lại, điều đó đúng - bạn chỉ cần cập nhật trường tần số.
Matthew Rodatus 4/12/2015

3

Trái với những gì người khác đang đề xuất, tôi đề nghị tránh mọi cấu trúc dữ liệu phức tạp hơn hàm băm hoặc kho lưu trữ khóa-giá trị.

Hãy ghi nhớ các yêu cầu truy cập dữ liệu của bạn: a) 99% yêu cầu - truy vấn ngram "aaa-bbb-ccc" và truy xuất giá trị (hoặc 0) b) 1% yêu cầu - chèn / cập nhật số lượng ngram cụ thể c) không có (c).

Cách hiệu quả nhất là lấy nó với một lần tra cứu. Bạn có thể sử dụng dấu phân tách ngoài giới hạn (hoặc thoát) để kết hợp toàn bộ n-gram trong một chuỗi (ví dụ: "alpha | beta | gamma" cho 3gram, "alpha" cho unigram, v.v.) và chỉ cần tìm nạp ( bằng hàm băm đó). Đó là cách khá nhiều phần mềm NLP làm điều đó.

Nếu dữ liệu ngram của bạn nhỏ (giả sử <1 gb) và phù hợp với bộ nhớ, thì tôi khuyên bạn nên sử dụng cấu trúc bộ nhớ trong chương trình hiệu quả (hashmap, cây, thử, v.v.) để tránh chi phí; và chỉ tuần tự hóa / deserialize thành các tập tin phẳng. Nếu dữ liệu ngram của bạn là terabyte trở lên, thì bạn có thể chọn các cửa hàng khóa-giá trị NoQuery được phân chia trên nhiều nút.

Để có hiệu suất cao hơn, bạn có thể muốn thay thế tất cả các từ ở mọi nơi bằng id số nguyên để thuật toán cốt lõi của bạn không thấy bất kỳ chuỗi (chậm) nào cả; sau đó nó hơi khác nhau để thực hiện cùng một ý tưởng.


1

Không phải là hiệu quả nhất, nhưng đơn giản và được kết hợp với cơ sở dữ liệu như bạn muốn:

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, ...)

wordpose nên có chỉ mục trên tài liệu và pos.

bigram là:

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

Sau đó, bạn có thể đếm () và nhóm theo cách của bạn với tần số và công cụ.

Để thay đổi thành bát quái, có thể dễ dàng tạo chuỗi này để bao gồm một word3.

Tôi đã thực hiện điều này trước đây (mặc dù SQL có lẽ hơi rỉ sét). Tôi giải quyết một tập hợp các tệp phẳng có thể tìm kiếm dễ dàng sau đó được truyền ra khỏi đĩa. Kinda phụ thuộc vào phần cứng của bạn làm thế nào để làm điều đó tốt hơn.


1

Trong khi cố gắng cải thiện các tìm kiếm đơn giản của ứng dụng của tôi thành bigram và trigram từ unigram, về cơ bản, tôi đã thấy câu hỏi của bạn.

Nếu một trong những yêu cầu là khả năng truy vấn hệ thống tệp hoặc cơ sở dữ liệu phân tán, thì điều này cũng có thể thú vị với bạn: Pibiri và Venturini 2018 trên giấy "Xử lý bộ dữ liệu N-Gram khổng lồ" một cách hiệu quả để lưu trữ dữ liệu n-gram trong điều khoản của thời gian chạy và không gian. Họ đã đề nghị thực hiện tại https://github.com/jermp/tongrams

Mỗi "n" của n-gram được giữ trong một bảng riêng biệt được truy cập bởi hàm băm hoàn hảo tối thiểu với khả năng chọn và truy vấn rất nhanh. Các bảng là tĩnh và được xây dựng bởi mã chính bằng cách sử dụng đầu vào ở định dạng của tệp văn bản Google n-gram.

Tôi chưa sử dụng mã này, nhưng có nhiều cách bạn có thể với các yêu cầu mở của bạn về nơi các truy vấn của bạn đến từ đâu.

Một cách: nếu tương đương .NET của một servlet được sử dụng với cơ sở dữ liệu hoặc kho dữ liệu và nếu bạn cần tiết kiệm dung lượng lưu trữ, thì lưu trữ mỗi bảng ngram ở dạng nhị phân trong cơ sở dữ liệu / kho dữ liệu dưới dạng bảng là một tùy chọn (một cơ sở dữ liệu / bảng kho dữ liệu cho tệp tĩnh kết quả của mã ngram hiệu quả cho tất cả 1 gram, một bảng khác cho tất cả 2 gram, v.v.). Các truy vấn sẽ được chạy bằng cách gọi mã n-gram hiệu quả (được bọc bởi servlet của bạn). Đó là một cách giải quyết để tạo một cơ sở dữ liệu phân tán đang sử dụng mã n-gram hiệu quả để truy cập các tệp trên hệ thống tệp phân tán. Lưu ý rằng mỗi bảng cơ sở dữ liệu nhị phân / kho dữ liệu đều có giới hạn kích thước tệp của hệ thống tệp cơ bản.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.