Test, train - NER i podział leksykalny

0

Hej.

Mam taką strukturę pliku:

[{'inforex_id': 215545},
 {'filename': 'en_banking_forums_0004'},
 {'raw': '"high yield savings" Omg, couldn\'t help myself laughing at that. Interest rates have been 0% (with negative real rates) for many many years. I forgot $10k in a savings account for about a decade in my Bank of America account and when I logged back in I had accrued a whooping <$10 of interest.Getting to your question:No, it\'s not too late AT ALL. Buy some but realize it\'s a highly volatile asset, so make sure you are comfortable experiencing an 80% drop in value (in fiat terms).Read the blogleheads book; asset allocation is an interesting and worthwhile topic to apply.'},
 {'labels': [{'name': 'Brand_Name',
    'user_id': '121',
    'stage': 'final',
    'value': 'Bank of America',
    'lemma': '',
    'start': 201,
    'end': 216},
   {'name': 'Location_Imp',
    'user_id': '121',
    'stage': 'final',
    'value': 'America',
    'lemma': '',
    'start': 209,
    'end': 216}]},
 {'relations': []},

'inforex_id' określa jeden tekst. W pliku jest 6006 takich id.
W 'labels' mam 'name', który określa nazwę własną-NER oraz 'value' czyli do czego w tekście odnosi się NER.

Muszę podzielić plik na dwa - treningowy i testowy w stosunku 9:1

W pierwszej kolejności jak największy rozdział leksykalny czyli moje 'value' -- jak najmniejszą część wspólna występujących tych samych wyrażeń (encji treściowych)
i jak najbardziej podobne rozkłady klas NER w obu zbiorach.

A podział, który spełnia tylko warunek podobieństwa rozkładów NER bez rozdziału leksykalnego.
Czasami jest tak, że w jednym tekście jest wiele NERów ('name')

Zaczynam tak:

df = pd.read_json('/home/korpus_EN_20220825.json')

df_cut = df['labels']

ner_list_value = []

for dic in df_cut:
    for i in dic:
        ner_list_value.append({i['name']:i['value']})

ner_list = []


for dic in df_cut:
    for i in dic:
        ner_list.append(i['name'])
        
data = pd.DataFrame(ner_list)

ner_df = data.rename(columns={0: "NER_class"})
group_ner = pd.DataFrame(ner_df, columns=['NER_class'])
ner_count_EN = group_ner.groupby(['NER_class']).agg({"NER_class" : "count"}).\
              rename(columns={'NER_class': 'count'}).\
              sort_values(by = ["count"], ascending = False)

print('Ilość NER_class w korpusie _EN ',len(ner_count_EN))

output: Ilość NER_class w korpusie _EN 20

Potem wycinam z df dwie kolumny, które są dla mnie istotne: id i labels i robię słownik, gdzie kluczem jest ID a wartościami NERy i ich leksykalne odpowiedniki żeby później po ID wyciągać z pliku konkretne teksty do podziału na test i train.

x - to ID
i - to już z 'labels'

df_a = df[['inforex_id','labels']]

t = []

for x ,dic in zip(df_a.inforex_id,df_a.labels ):
    for i in dic:
        t.append({x: [i['name'],i['value']]})

output:

[{215545: ['Brand_Name', 'Bank of America']},
 {215545: ['Location_Imp', 'America']},
 {215547: ['Brand_Name', 'BofA']},
 {215685: ['Brand_Name', 'Fidelity']},
 {215685: ['Brand_Name', 'CMA']},
 {215685: ['Brand_Name', 'Bank of America']},
 {215685: ['Brand_Name', 'BofA']},
 {215685: ['Brand_Name', 'CMA']},
 {215685: ['Brand_Name', 'Fidelity']},
 {215685: ['Brand_Name', 'Chase']},
 {215685: ['Brand_Name', 'Fidelity']},
 {215685: ['Brand_Name', 'BofA']},
 {215685: ['Brand_Name', 'CMA']},
 {215685: ['Brand_Name', 'Discover']},
 {215685: ['Brand_Name', 'BofA']},
 {215685: ['Brand_Name', 'BofA']},
 {215685: ['Brand_Name', 'Fidelity']},
 {215685: ['Location_Imp', 'America']},
 {215687: ['Brand_Name', 'Bangkok Bank']},
 {215687: ['Brand_Name', 'Federal Reserve Bank of New York']},

i tutaj trochę stoję w miejscu jak ten podział zrealizować zgodnie z założeniami ?

edit:

dodatkowo zrobiłem agregację po kluczu>

from collections import defaultdict

res = defaultdict(list)

for l in t:
    for key, val in sorted(l.items()):
        res[key].append([val])
        
res

output:

defaultdict(list,
            {215545: [[['Brand_Name', 'Bank of America']],
              [['Location_Imp', 'America']]],
             215547: [[['Brand_Name', 'BofA']]],
             215685: [[['Brand_Name', 'Fidelity']],
              [['Brand_Name', 'CMA']],
              [['Brand_Name', 'Bank of America']],
              [['Brand_Name', 'BofA']],
              [['Brand_Name', 'CMA']],
              [['Brand_Name', 'Fidelity']],
              [['Brand_Name', 'Chase']],
              [['Brand_Name', 'Fidelity']],
              [['Brand_Name', 'BofA']],
              [['Brand_Name', 'CMA']],
              [['Brand_Name', 'Discover']],
3

Nie bardzo rozumiem jaki jest problem.
Ale np. jeżeli chodzi o prosty podział na dwie listy to można zastosować po prostu poniższą konstrukcję:

a)
Np. jest 100 elementów w data.

train_data = data[:90]
test_data = data[90:] 

Można np. potasować jeszcze przed tym wszystkim zbiór:

random.shuffle(data)

b) albo train_test_split ( https://scikit-learn.org/stable/modules/generated/sklearn.model_selection.train_test_split.html)
c) albo w ML można i warto stosować cross walidację, Zbiór wtedy dzieli się przy pomocy np.: KFold lub StratifiedKFold ( https://scikit-learn.org/stable/modules/generated/sklearn.model_selection.StratifiedKFold.html )

0

Czy możesz zmienić strukturę pliku?

0
_13th_Dragon napisał(a):

Czy możesz zmienić strukturę pliku?

Na potrzeby preprocesingu tak.
Ostatecznie muszę mieć podział po ID.

0

To może prościej.
Podział na dwa zbiory w stosunku 9:1, żeby rozkład klas NER był podobny procentowo.

?

2

1 - train_test_split ( https://scikit-learn.org/stable/modules/generated/sklearn.model_selection.train_test_split.html) - trzeba wtedy użyć parametru stratify
2 - StratifiedKFold ( https://scikit-learn.org/stable/modules/generated/sklearn.model_selection.StratifiedKFold.html ) - użyć parametru n_splits=10

Obie te funkcje dzielą zbiór w taki sposób, żeby rozkład wartości oczekiwanej był podobny.

1
Robert Karpiński napisał(a):

Obie te funkcje dzielą zbiór w taki sposób, żeby rozkład wartości oczekiwanej był podobny.

Tak, tak wiem, dzisiaj jak tylko przyjdę do pracy to przetestuję :)

Zobaczę tylko czy jako zbiór przyjmie defaultdict bo nie mogę rozdzielić tekstów (id) żeby po podziale wyciągnąć całe słowniki z wartościami, które mam w strukturze pliku.

Chyba, że jest sensowniejsza metoda pogrupowania po ID?

1 użytkowników online, w tym zalogowanych: 0, gości: 1