Gå til innhold

Anbefalte innlegg

Jeg har en (relativt stor) tekstfil som jeg deler opp i ord og genererer n-grammer av lengder fra og med 1 til og med n fra.

 

Deretter lagrer jeg informasjon i rekker i to kolonner hvor den første kolonnen inneholder et ngram, og den andre kolonnen inneholder et ord som fulgte rett etter ngrammet i teksten.

 

 

så "a a b c d e" for n = 2 blir da omtrent:

Tabell 1:
(a,) --> a
(a,) --> b
(b,) --> c
(c,) --> d
(d,) --> e

Tabell 2:
(a, a) --> b
(a, b) --> c
(b, c) --> d
(c, d) --> e

 

Problemet er at det tar litt vel lang tid å skrive til og lese fra denne databasen når den begynner å få litt lengde.

 

Dersom jeg ikke har tenkt til å gjøre annet enn

SELECT word FROM tablename WHERE ngram=?

og

INSERT OR IGNORE INTO tablename VALUES (?,?)

 

Lønner det seg å ha flere databaser, for eksempel en database for hver bokstav målt etter hvilken bokstav det første elementet i hvert ngram har slik at hver databasefil blir mindre?

 

Jeg antar her at det tar lang tid når databasen blir lang fordi den må dytte på så mye hver gang den skal legge til noe. Eller noe sånn.

Endret av Yumekui
Lenke til kommentar
Videoannonse
Annonse

Konstant skriving til databasen, og mange rader vil altid gjøre det tregere. I noen tilfeller kan det lønne seg å bruke faltfiler, men vet ikke om dette er ett av dem. mysql vil fort sluke mye ram når den er høyt belastet.

 

Kunne gjerne tenkt meg at du spesifiserte litt mer om kolonnene, er det int, varchar, textarea etc, og størrelsen på disse er? Er det korte tekster burde størrelsen være helt minimal.

 

Hvor langt mener du dette blir, og hvor mye pushes inn i løpet av kort tid har også en del å si. Du kan fort klare å gjøre databasen for treg etter 20,000 rader. Er det enkel data som skal catches så er vel ikke SQL den beste veien å gå.

 

Uten noen som helst forståelse for hva dette gjelder så vil jeg bare notere:

Jeg bruker av og til å jukse, dette er for å unngå at den må lese så utrolig mye (dette gjelder bare om en skal hente ut f.eks 500 rader samtidig). Du kan pakke mye data inn i en kolonne ved å seprarere data med div. tegn. for å så bruke split() når data er hentet ut. Det negatve er at det blir fort mer komplisert, og uoversiktelig.

Endret av warpie
Lenke til kommentar

 

Hvert ngram er opprinnelig en Python-tuppel bestående av unicode strenger. Jeg omgjør hver tuppel til en streng før jeg legger de i den første kolonnen. Den andre kolonnen inneholder også strenger (ett enkelt ord per rad).

Så hver rad er liten, men antallet rader kommer fort til å overstige 100 000.

Det er ønskelig at jeg kan hente ut ordet i kolonne ved å bare vite ngrammet i kolonne 1

SELECT word FROM tablename WHERE ngram=?

ordner dette for øyeblikket.

 

 

 

Innholdet blir seende noe slikt ut:

 

 


x = c.execute("Select * from table_name")
for a in sorted(x):
print a
#-----
...
(u"(u'a', u'$')", u'3')
(u"(u'a', u'$')", u'400')
(u"(u'a', u'.')", u'barret')
(u"(u'a', u'.')", u'douglas')
(u"(u'a', u'.')", u's')
(u"(u'a', u'11%')", u'tax')
(u"(u'a', u'14%')", u'average')
(u"(u'a', u'2')", u',')
...
(u"(u'a', u'way')", u'to')
(u"(u'a', u'wealthy')", u'slave-holding')
(u"(u'a', u'wedding')", u'was')
(u"(u'a', u'while')", u',')
(u"(u'a', u'wide')", u'spectrum')
(u"(u'a', u'workforce')", u'of')
(u"(u'a', u'year')", u'before')
(u"(u'a', u'young')", u'west')
(u"(u'aba', u')')", u',')
...
(u"(u'about', u'0')", u'.')
(u"(u'about', u'0\\xb0c')", u')')
(u"(u'about', u'10%')", u'medical')
(u"(u'about', u'10')", u':')
(u"(u'about', u'120')", u',')
(u"(u'about', u'15\\xb0c')", u'.')
(u"(u'about', u'1\–2')", u'per')
...
(u"(u'achilles', u'through')", u'his')
(u"(u'achilles', u'to')", u'chiron')
(u"(u'achilles', u'to')", u'heal')
(u"(u'achilles', u'to')", u'their')
(u"(u'achilles', u'was')", u'cremated')
(u"(u'achilles', u'was')", u'once')
(u"(u'achilles', u'was')", u'scaling')
...
(u"(u'adequate', u'understanding')", u'of')
(u"(u'adequate', u'yearly')", u'progress')
(u"(u'aderholt', u',')", u'morris')
(u"(u'adhered', u'to')", u'the')
(u"(u'adherents', u'.')", u'a')
(u"(u'adherents', u'in')", u'alabama')
(u"(u'adhesion', u'.')", u'gene')
(u"(u'adi-r', u')')", u'is')
(u"(u'adjustments', u',')", u'or')
(u"(u'administration', u',')", u'cabinet')
(u"(u'administration', u'offered')", u'him')
(u"(u'administrations', u'were')", u're-formed')
(u"(u'admiration', u'of')", u'and')
(u"(u'admission', u'of')", u'either')
(u"(u'admitted', u'october')", u'31')
(u"(u'admitted', u'to')", u'the')
(u"(u'adolescence', u',')", u'but')
(u"(u'adolescents', u'and')", u'adults')
(u"(u'adopted', u'a')", u'constitution')
(u"(u'adopted', u'the')", u'alphabet')
(u"(u'adopted', u'the')", u'etruscan')
(u"(u'adopting', u'a')", u'revolutionary')
(u"(u'ados', u')')", u'uses')
(u"(u'adtran', u',')", u'computer')
(u"(u'adult', u'levels')", u'.')
(u"(u'adult', u'onset')", u'diabetes')
(u"(u'adult', u'residential')", u'programs')
(u"(u'adulthood', u',')", u'although')
(u"(u'adulthood', u',')", u'though')
(u"(u'adults', u',')", u'10%')
...
(u"(u'airport', u'(')", u'mob')
(u"(u'airport', u'(')", u'msl')
(u"(u'airport', u'(')", u'tcl')
...
#(etc)

 

 

Endret av Yumekui
Lenke til kommentar

Bruker indekser, ja - Jeg vet ikke om transaksjoner brukes eller ikke.

 

Det er denne delen som tar lang tid i större databaser:

query = u"INSERT OR IGNORE INTO {TABLENAME} VALUES (?,?)".format(TABLENAME=tablename)
try:
   self.brain.execute(query, (ngram, word))

 

 

import sqlite3
class DiskFormat(object):
"""Fileformat is an SQLite database"""
def load(self):
	self.conn = sqlite3.connect("@brain.db")
	self.brain = self.conn.cursor()
	self.MakeTables()

def save(self, keepold=False):
	self.conn.commit()


def _ExecuteMakeTable(self, tablename):
	self.brain.execute('''CREATE TABLE {name}
						  (ngram text, word text)'''.format(name=tablename))
def _ExecuteMakeIndice(self, tablename):
	print "Creating unique index for:",tablename
	self.brain.execute('''CREATE UNIQUE INDEX ind_{name}
						  ON {name} (ngram, word)'''.format(name=tablename))

def MakeTables(self):
	"""Called to make sure enough tables to meet requirement of current n exist"""
	#keep track of ngrams up to and including this length
	n = config["ngrams"]	  
	for db in range(1, n+1):
		try:
			tablename = "precedes_{0}".format(db)
			self._ExecuteMakeTable(tablename)
			self._ExecuteMakeIndice(tablename)

		except sqlite3.OperationalError as sqlerr:
			if "already exists" in str(sqlerr):
				pass
			else:
				raise

		try:
			tablename = "succeeds_{0}".format(db)
			self._ExecuteMakeTable(tablename)
			self._ExecuteMakeIndice(tablename)

		except sqlite3.OperationalError as sqlerr:
			if "already exists" in str(sqlerr):
				pass
			else:
				raise

def DB_Write(self, tablename, ngram, word):
	"""Add word as related to ngram in table"""
	ngram = unicode(ngram)
	query = u"INSERT OR IGNORE INTO {TABLENAME} VALUES (?,?)".format(TABLENAME=tablename)
	try:
		self.brain.execute(query, (ngram, word))
		pass
	except sqlite3.IntegrityError as interr:
		if "are not unique" in str(interr):
			pass
		else:
			raise

Endret av Yumekui
Lenke til kommentar

Det ser ut som du bruker transaksjoner, men det spørs hvor ofte du kaller save().

Når du skriver til databasen prøv å kjør kanskje 1000-10000 DB_Write() mellom hver gang du kaller save(), prøv deg fram for å finne det riktige antallet. Det skal gjøre skrivingen en del raskere hvis du ikke allerede gjør det.

 

Du kan kanskje også prøve å finne en bedre måte å lagre ngram verdien, nå er det veldig mye repeterende data med "(u'" i hver rad.

Lenke til kommentar

Det ser ut som du bruker transaksjoner, men det spørs hvor ofte du kaller save().

Når du skriver til databasen prøv å kjør kanskje 1000-10000 DB_Write() mellom hver gang du kaller save(), prøv deg fram for å finne det riktige antallet. Det skal gjøre skrivingen en del raskere hvis du ikke allerede gjør det.

 

Du kan kanskje også prøve å finne en bedre måte å lagre ngram verdien, nå er det veldig mye repeterende data med "(u'" i hver rad.

 

Jeg kalte ikke save i det hele tatt, egentlig. Jeg gjorde det helt til slutt. Er det noe slik som for sjeldent?

 

Ved ettertanke bruker jeg ikke ngrammene til noe annet enn å hente ut ordene, så jeg kan sikkert lagre de på en annen måte som for eksempel mellomrom-separerte verdier.

Lenke til kommentar
  • 2 uker senere...

Hvor mye data er det vi snakker om? Og hvor tregt er tregt? Og hvor mange duplikater har du - dvs. hvor mange exceptions generer scriptet ditt?

Datamengden er en tekstfil med et par millioner linjer (hver av setningene gjøres om til ngrammer)

 

I begynnelsen går det rimelig raskt (1000 setninger i sekundet~), men ettersom databasen blir større og større går det tregere og tregere, og når antallet prosesserte setninger er oppe i 30000-50000 tar det 40~ sekunder for 1000 setninger. Tabellen succeeds_2 inneholder 2 066 156 rekker etter 40 000 setninger (av en eller annen grunn tar det lenger tid å fylle tabeller med ngrammer av større n-verdier).

 

Jeg vet ikke hvordan det er med duplikatmengden. Jeg antar det blir flere og flere ettersom antallet entries øker.

 

 

 

Lagde et ganske rotete skript for å mate linjer fra tekstfilen til programmet (i dette tilfellet 1000 og 1000 linjer av gangen), disse linjene gjøres om til ngrammer og puttes i en [(ngram, ord), (ngram, ord), ...] liste, og hele denne listen gis til executemany (så med mindre jeg tuller med noe skulle ikke for mange kall til execute være problemet)

from engine import main
import time
def Learn3(bulk):
L = []
i = 0
with open("learning/LineFile.txt") as f:
	for line in f:
		line = line.decode("utf8")
		line = line.strip()
		L.append(line)

		if len(L) == bulk:
			print "\ncurrent:",i
			i += bulk
			contents = " ".join(L)

			a = time.time()
			proc.QuickLearn(contents, "abc", "def")
			print bulk,"in:",time.time() - a

			L = []
	#Do the last of L
	contents = " ".join(L)
	proc.QuickLearn(contents, "abc", "def")

proc = main.Engine()
Learn3(1000)
braintf.save()

 

 

Selve databasen lages og skrives til slik (det er den siste linjen,

self.brain.executemany(u"INSERT OR IGNORE INTO {TABLENAME} VALUES (?,?)".format(TABLENAME=tablename), ngwl)

som tar tid (og den tar lenger tid jo større n (antall ord i lengste ngram) blir)):

 


class DiskFormat(object):
"""Fileformat is an SQLite database"""
def load(self):
	dbpath = "database" + sep + "@brain.db"
	self.conn = sqlite3.connect(dbpath)
	self.brain = self.conn.cursor()
	self.MakeTables()
def save(self, keepold=False):
	self.conn.commit()

def _ExecuteMakeTable(self, tablename):
	self.brain.execute('''CREATE TABLE {name}
						  (ngram text, word text)'''.format(name=tablename))

def _ExecuteMakeIndice(self, tablename):
	print "Creating unique index for:",tablename
	self.brain.execute('''CREATE UNIQUE INDEX ind_{name}
						  ON {name} (ngram, word)'''.format(name=tablename))

def MakeTables(self):
	"""Called to make sure enough tables to meet requirement of current n exist"""
	#keep track of ngrams up to and including this length
	n = config["ngrams"]	  
	for db in range(1, n+1):
		try:
			tablename = "precedes_{0}".format(db)
			self._ExecuteMakeTable(tablename)
			self._ExecuteMakeIndice(tablename)

		except sqlite3.OperationalError as sqlerr:
			if "already exists" in str(sqlerr):
				pass
			else:
				raise

		try:
			tablename = "succeeds_{0}".format(db)
			self._ExecuteMakeTable(tablename)
			self._ExecuteMakeIndice(tablename)

		except sqlite3.OperationalError as sqlerr:
			if "already exists" in str(sqlerr):
				pass
			else:
				raise
def DB_Write(self, tablename, ngram_word_list):
	"""Takes a list of ngram-word tuples and inserts them into &--#60;tablename&--#62;
	   input: [(ngram, word), ...]
	"""
	ngwl = [(u" ".join(ngram), word) for ngram,word in ngram_word_list]
	#try:
	self.brain.executemany(u"INSERT OR IGNORE INTO {TABLENAME} VALUES (?,?)".format(TABLENAME=tablename), ngwl)
	"""
	except sqlite3.IntegrityError as interr:
		if "are not unique" in str(interr):
			pass
		else:
			raise
	"""

Endret av Yumekui
Lenke til kommentar

Hvis du lar være å opprette indexen før du inserter dataen vil det mest sannsynlig gå en del raskere. Det er ikke sikkert det har noe å si om det blir satt inn noen duplikater, eller du kan prøve å fjerne dem etterpå.

Det ser ut til å ha hjulpet. Ganske konstant skrivehastighet nå etter hva jeg kan se. Duplikater har noe å si, men det burde gå greit å fjerne disse i ettertid.

Lenke til kommentar

Du kan legge inn indexen etterpå. Men hvis du har duplikater så kan du ikke legge inn en unique index, bare en vanlig index.

Dette er en helt vanlig strategi ved batch-oppdatering av data - fjerne index, gjøre masse endringer, og så bygge indexene på nytt. Fungerer bra sålenge dataene ikke samtidig skal være tilgjengelige for spørringer :)

Endret av torbjørn marø
Lenke til kommentar
  • 2 uker senere...

Opprett en konto eller logg inn for å kommentere

Du må være et medlem for å kunne skrive en kommentar

Opprett konto

Det er enkelt å melde seg inn for å starte en ny konto!

Start en konto

Logg inn

Har du allerede en konto? Logg inn her.

Logg inn nå
×
×
  • Opprett ny...