Upload
others
View
2
Download
0
Embed Size (px)
Citation preview
INF2820 Datalingvistikk – V2014 3. forelesning, 30.1 Jan Tore Lønning
Idag
• Noen ord om Python • Implementasjon av DFA
• J&Ms algoritme • Oversatt til Python • Rekursiv vs. Iterativ implementasjon
• Naiv NFA-algoritme • Pythonimplementasjon
• En bedre NFA-algoritme • Pythonimplementasjon
2 29. januar 2014
Verktøy
NLTK: The Natural Language Toolkit • Programmer for ulike typer NLP-
oppgaver • Kan kombinere med egen kode • Vekt på opplæring:
• Men også brukt for større oppgaver • Bok, dokumentasjon
Endelige tilstandsteknikker • De første ukene
29. januar 2014 3
Python • Gode strukturer for tekst:
• Strenger • Lister
• Read-eval-print-loop • Lesbar, strukturert kode:
• Kompakt, nesten ”pseudokode” • Gir gode programmeringsvaner • Lett å lære
• Objektorientert • Mye brukt: tilgjengelig, bibliotek, grensesnitt • Nyttig senere i livet: scripting
29. januar 2014 4
Vi antar at alle nå kan
(Fra et eller annet p.språk) • Variable og
verdier • Tilordninger og andre
kommandoer • Booleske tester • If-uttrykk • While-løkker • For-løkker • Funksjoner (sub-rutiner)
And how to do it in Python • a, bcd, alpha114
5, 5.0, ”abc”, …. • a = 5, a = a+5
print a • a == b, a >=b, .. • if a == b: c = c+2 • while… • for… • Functions
5
Python syntaks
• Python bruker indent som blokkavgrenser: • Andre språk: begin-end, ( ) • Hva tror du blir resultatet av kallene
• f(8) • g(8)
6
def f(i): for j in [2,3,4]: i=j+i print i
def g(i): for j in [2,3,4]: i=j+i print i
Python datatyper
• integer • float • string:
• ”Hello world” • lister:
• [3, 4, 5] • [’Hello’, ’world’] • [3, 4, c] • Aksesseres med
indekser • ”mutable”
7
>>> a = "Hello world" >>> a 'Hello world' >>> len(a) 11 >>> a[0] 'H' >>> a[-1] 'd' >>> b = a[3:7] >>> b 'lo w' >>> type(b) <type 'str'>
>>>c = 10 >>>e = [3,4,c,c+c,5*c] >>>e [3,4,10,20,50] >>>e[3] = 19 >>>e [3,4,10,19,50] >>>f = e[1:] >>>f [4,10,19,50] >>>e[3]=f >>>e [3,4,10,[4,10,19,50],50] >>>f[2]=0 >>>e ?
Python er objektorientert
• Alt er objekter • Som har sine metoder • Eksempler med strenger:
• ”Hello world,”.split() • ”world,”.strip(’,’)
• Vi skal ikke gjøre så mye bruk av dette i egen
kode • Men vi vil se mange innebygde objekter i NLTK
8
Idag
• Noen ord om Python • Implementasjon av DFA
• J&Ms algoritme • Oversatt til Python • Rekursiv vs. Iterativ implementasjon
• Naiv NFA-algoritme • Pythonimplementasjon
• En bedre NFA-algoritme • Pythonimplementasjon
9 29. januar 2014
Implementering av DFA-gjenkjenning
10
Jurafsky & Martin, fig. 2.13
DFA i Python
11
def drec(tape, dfa): index = 0 state = dfa.start while True: if index == len(tape): if state in dfa.finals: return "accept" else: return "reject" elif not (state, tape[index]) in \
dfa.edge.keys(): return "reject" else: state = dfa.edge[(state,tape[index])] index += 1
DFA i Python – datastruktur
12
def drec(tape, dfa): index = 0 state = dfa.start while True: if index == len(tape): if state in dfa.finals: return "accept" else: return "reject" elif not (state, tape[index]) in \
dfa.edge.keys(): return "reject" else: state = dfa.edge[(state,tape[index])] index += 1
class DFA:
def __init__(self):
self.edge = {}
self.finals = []
f = DFA()
f.start = 0
f.finals.append(4)
f.edge[(0,'b')] = 1
f.edge[(1,'a')] = 2
f.edge[(2,'a')] = 3
f.edge[(3,'a')] = 3
f.edge[(3,'!')] = 4
DFA i Python – datastruktur
13
def drec(tape, dfa): index = 0 state = dfa.start while True: if index == len(tape): if state in dfa.finals: return "accept" else: return "reject" elif not (state, tape[index]) in \
dfa.edge.keys(): return "reject" else: state = dfa.edge[(state,tape[index])] index += 1
class DFA:
def __init__(self):
self.edge = {}
self.finals = []
f = DFA()
f.start = 0
f.finals.append(4)
f.edge[(0,'b')] = 1
f.edge[(1,'a')] = 2
f.edge[(2,'a')] = 3
f.edge[(3,'a')] = 3
f.edge[(3,'!')] = 4
Forenklet struktur Bedre praksis • Lag rutiner i klassen for å konstruere objekt • Beskytt objektenes indre • Legg anerkjenning som en metode I
klassen
Rekursion vs. iterasjon
14
def drec(tape, dfa): index = 0 state = dfa.start while True: if index == len(tape): if state in dfa.finals: return "accept" else: return "reject" elif not (state, tape[index]) in \
dfa.edge.keys(): return "reject" else: state = dfa.edge[(state,tape[index])] index += 1
def drecrec(tape, dfa): state = dfa.start return recrec(tape, dfa, state) def recrec(tape, dfa, state): if len(tape) == 0: if state in dfa.finals:
return "accept"
else:
return "reject" elif (state, tape[0]) not in \
dfa.edge.keys(): return “reject” else: state = dfa.edge[(state,tape[0])] return recrec(tape[1:], dfa, state)
Idag
• Noen ord om Python • Implementasjon av DFA
• J&Ms algoritme • Oversatt til Python • Rekursiv vs. Iterativ implementasjon
• Naiv NFA-algoritme • Pythonimplementasjon
• En bedre NFA-algoritme • Pythonimplementasjon
15 29. januar 2014
16
Søkerom
17
•Breddeførst søk •JFLAP
18
•Dybdeførst søk •m/ Backtracking
Husk rekursiv DFA-rec
19
def drec(tape, dfa): index = 0 state = dfa.start while True: if index == len(tape): if state in dfa.finals: return "accept" else: return "reject" elif not (state, tape[index]) in \
dfa.edge.keys(): return "reject" else: state = dfa.edge[(state,tape[index])] index += 1
def drecrec(tape, dfa): state = dfa.start return recrec(tape, dfa, state) def recrec(tape, dfa, state): if len(tape) == 0: if state in dfa.finals:
return "accept"
else:
return "reject" elif (state, tape[0]) not in \
dfa.edge.keys(): return “reject” else: state = dfa.edge[(state,tape[0])] return recrec(tape[1:], dfa, state)
Naive NFA-anerkjenning
20
def naivenrec(tape, nfa):
state = fa.start
return rec(tape, nfa, state)
def rec(tape, nfa, state):
if len(tape) == 0:
if state in nfa.finals:
return "accept"
else:
return "reject"
else:
states = [e[2] for e in nfa.edges
if state == e[0] and tape[0] == e[1] ]
for state in states:
if rec(tape[1:],nfa,state): return“accept”
return “reject”
def drecrec(tape, dfa): state = dfa.start return recrec(tape, dfa, state) def recrec(tape, dfa, state): if len(tape) == 0: if state in dfa.finals:
return "accept"
else:
return "reject" elif (state, tape[0]) not in \
dfa.edge.keys(): return “reject” else: state = dfa.edge[(state,tape[0])] return recrec(tape[1:], dfa, state)
Alle mulige neste tilstander
Alle mulige forgreninger
Så langt: NFA uten ε-transisjoner
NFA datastruktur
21
def naivenrec(tape, nfa):
state = fa.start
return rec(tape, nfa, state)
def rec(tape, nfa, state):
if len(tape) == 0:
if state in nfa.finals:
return "accept"
else:
return "reject"
else:
states = [e[2] for e in nfa.edges
if state == e[0] and tape[0] == e[1] ]
for state in states:
if rec(tape[1:],nfa,state): return“accept”
return “reject”
class NFA: def __init__(self): self.edges = [] self.finals = [] … f = NFA(…) f.start = 0 f.finals.append(4) f.edges= [ (0,'b',1), (1,'a',2), (2,'a',3), (3,'a',3), (3,'!',4) ] g=NFAFromFile('template.nfa')
Så langt: NFA uten ε-transisjoner
Python: ”list comprehension” edges = [e for e in fa.edges if state == e[0] and streng[0] == e[1] ]
edges = []
for e in fa.edges:
if state == e[0] and streng[0] ==e[1]:
edges.append(e)
22
{ }]0[]1[]0[|. strengestateeedgesfaeE =∧=∈=
Python: ”list comprehension” states = [e[2] for e in fa.edges
if state == e[0] and streng[0] == e[1] ]
states = [e[2] for e in fa.edges if state==e[0] and streng[0]==e[1] ]
states = []
for e in fa.edges:
if state==e[0] and streng[0]==e[1]:
states.append(e[2])
23
{ }{ }]0[]1[]0[|.|]2[ strengestateeedgesfaeeeS =∧=∈∈=
{ }EeeS ∈= |]2[
Jurafsky og Martins algoritme
24
Bruker løkke+agenda
i stedet for rekursjon
Men samme •hovedstruktur
•tidsforbruk
Egenskaper ved algoritmene • Både
• dybde-først m/backtracking • breddeførst vil i verste fall ha eksponentielt tidsforbruk proporsjonalt med
• kn, der • n= |w|, lengden av input • k≥2 er maks antall kanter fra en node merket med samme symbol
• Med epsilontransisjoner • Kan risikere ikke terminerer!
• Men vi vet jo at hvis vi først lager DFA får vi linjært tidsforbruk!
25
Idag
• Noen ord om Python • Implementasjon av DFA
• J&Ms algoritme • Oversatt til Python • Rekursiv vs. Iterativ implementasjon
• Naiv NFA-algoritme • Pythonimplementasjon
• En bedre NFA-algoritme • Pythonimplementasjon
26 29. januar 2014
En raskere algoritme
• En konfigurasjon består av: • En mengde tilstander • Resten av strengen
• Start: • Q0 = E({q0})
• (E er epsillontillukning) • Oppdatering
• Gitt konfigurasjon: • wn = s⋅w’ • Qn={q1, …, qk}
• La ny konfigurasjon være • wn+1 = w’ • Qn+1=E(δN(q1,s)∪δN(q2,s)∪…∪ δN(qk,s))
• Akseptering • Konfigurasjonen
• wn = ε • Qn={q1, …, qk}
Aksepterer hvis minst en av q1, …, qk er en sluttilstand. 27
Smart NFA-anerkjenning i Python
28
def drec(tape, dfa): index = 0 state = dfa.start while True: if index == len(tape): if state in dfa.finals: return "accept" else: return "reject" elif not (state, tape[index]) in \
dfa.edge.keys(): return "reject" else: state = dfa.edge[(state,tape[index])] index += 1
NFA DFA
def nrec(tape, nfa):
index = 0
states = [nfa.start]
while True:
if index == len(tape):
successtates = [s for s in states
if s in nfa.finals]
return len(successtates)> 0
elif len(states) == 0:
return False
else:
states = set([e[2] for e in nfa.edges
if e[0] in states and
tape[index] == e[1] ])
index += 1
Egenskaper
• Svarer til underveis å bygge de delene vi trenger av DFA-ene som svarer til denne NFA-en.
• Algoritmen er linjær i |w|=n. • Men kvadratisk i antall tilstander: m • O(n m**2) • Terminerer
29
Ta med hjem:
• Gitt en NFA: N som beskriver et språk L=L(N) • Da finnes det en DFA: D som beskriver samme språk,
L=L(D) • Skal vi implementere N, kan vi enten konstruere D
(forrige gang) • Eller prosessere direkte med N (som om det var D) • Uansett er prosedyren
• Ikke flertydig • Deterministisk • Tidsforbruket er linjært i input
30
REGULÆRE UTTRYKK I PRAKSIS
31
Regulære uttrykk – to tilnærminger
Teoretisk • Sett på så langt • Oprinnelig (1950-tallet) • J&M seksj 2.3 • Tilstreber:
• Minst mulig notasjon for å definere klassen
• Formelt meget veldefinert
Praktisk • ”RegEx” • Unix (grep/egrep), Perl,
Emacs, … • Tilstreber effektiv i bruk • Spesialsymboler, div.
forkortelser. • MEN: kan inneholde
muligheter som går ut over de regulære språkene!
32
Forskjeller til teoretiske RE • Vi beskriver ikke et språk men søker etter substrenger av et
språk • Ankere
• ^ begynnelsen av linjen • $ slutten av linjen • Går ut over rene RE
• Muligheten til å referere tilbake til hele grupper: • Går utover regulære språk • Kan ikke uten videre bruke DFA som algoritme
33
Implementasjon av regex
3. Backtracking: • En prøver å matche regex direkte mot (et segment
av) strengen • Leser regex fra venstre mot høyre
• (tilpasset for * + …) • Ser om en kan utvide strengsegmentet til å matche
neste symbol i regex • Hvis ikke: backtrack – gå tilbake på tidligere valg
SØK: finn et delsegment av strengen som matcher OBS: Regex går også utover kontekstfrie språk 34
Implementasjon av regex
• Hvis ekte regulært uttrykk: • Gjør om til NFA • Bruk algoritme 1 eller 2
• Hvis regex går utover regulære uttrykk er det vanlige
• Bruk algoritme av type 3
35