Training Model
This commit is contained in:
+23
-21
@@ -17,10 +17,10 @@ import tempfile
|
||||
import time
|
||||
from fractions import Fraction as F
|
||||
|
||||
from generator import accompaniment, comping, fretboard, model, musicxml, parts, rhythm, score, vocabulary, resources as catalogs
|
||||
from generator import accompaniment, comping, fretboard, model, musicxml, parts, score, vocabulary
|
||||
from generator.arrange import form, generate, pickup
|
||||
from generator.solo import analysis, composer, notation, report, sequences
|
||||
from generator.scales import library as scales
|
||||
from generator.solo import analysis, composer, notation, report, learned
|
||||
from generator.solo import authored as repertoire
|
||||
|
||||
VERSION = 'independent-roles-1'
|
||||
ROLES = ('acoustic_accompaniment', 'electric_accompaniment', 'electric_melody', 'vocal_melody', 'acoustic_solo', 'electric_solo')
|
||||
@@ -78,7 +78,7 @@ def windows(document, part):
|
||||
|
||||
|
||||
def dependencies(role, resources):
|
||||
names = ('solo', 'harmony', 'lead_ins', 'rhythms') if role.endswith('_solo') else ('rhythms',) if role.endswith('_accompaniment') else ()
|
||||
names = ('harmony', 'lead_ins') if role.endswith('_solo') else ('accompaniment',) if role.endswith('_accompaniment') else ()
|
||||
return {'roles': {'role': resources['roles']['roles'][role], 'difficulty': resources['roles']['difficulty']},
|
||||
**{name: resources[name] for name in names}}
|
||||
|
||||
@@ -88,8 +88,8 @@ def _code_key(role):
|
||||
paths = [root / n for n in ('production.py', 'model.py', 'harmony.py', 'pitch.py', 'fretboard.py', 'parts.py', 'score.py', 'vocabulary.py')]
|
||||
paths += list((root/'arrange').glob('*.py'))
|
||||
if role.endswith('_solo'):
|
||||
paths += [p for p in (root/'solo').glob('*.py') if 'audition' not in p.name and p.name != 'references.py']
|
||||
paths += list((root/'scales').glob('*.py')) + [root/'rhythm.py', root/'resources.py', root/'musicxml.py']
|
||||
paths += list((root/'solo').glob('*.py'))
|
||||
paths += [root/'harmonic_time.py', root/'musicxml.py',root/'training_io.py']
|
||||
elif role.endswith('_accompaniment'):
|
||||
paths += [root/n for n in ('comping.py', 'accompaniment.py', 'guitar.py')]
|
||||
elif role == 'electric_melody':
|
||||
@@ -98,12 +98,11 @@ def _code_key(role):
|
||||
|
||||
|
||||
def _resource_key(directory, role):
|
||||
names = ('scales', 'sequences', 'rhythms') if role.endswith('_solo') else ()
|
||||
return {name: vocabulary.fingerprint([(p.relative_to(directory/name).as_posix(), hashlib.sha256(p.read_bytes()).hexdigest()) for p in sorted((directory/name).rglob('*.xml'))]) for name in names}
|
||||
return {'model': hashlib.sha256((directory/'models'/'solo.json').read_bytes()).hexdigest()} if role.endswith('_solo') else {}
|
||||
|
||||
@functools.lru_cache(maxsize=4)
|
||||
def _solo_libraries(directory, fingerprints, cache_dir=None):
|
||||
return catalogs.load(directory,dict(fingerprints),cache_dir)
|
||||
def _solo_model(directory, fingerprint):
|
||||
return learned.Model.load(Path(directory)/'models'/'solo.json')
|
||||
|
||||
|
||||
def _mask(document, allowed):
|
||||
@@ -153,7 +152,7 @@ def generate_role(source, role, data_dir, settings=None, cache_dir=None):
|
||||
with composition(data_dir,role,cache_dir) as reads:
|
||||
result=_generate_role(source,role,data_dir,settings,cache_dir)
|
||||
# Observed reads are diagnostics, never cache identity or musical seeds.
|
||||
result['trace']={**result['trace'],'input_boundary':'data, generator code, Python runtime, own role cache, content-addressed immutable catalogs'}
|
||||
result['trace']={**result['trace'],'input_boundary':'production data (including explicit authored repertoire), generator code, Python runtime, own role cache'}
|
||||
result['observed_reads']=sorted(reads)
|
||||
result['seconds']=time.monotonic()-started
|
||||
return result
|
||||
@@ -163,10 +162,10 @@ def _generate_role(source, role, data_dir, settings=None, cache_dir=None):
|
||||
if role not in ROLES: raise ValueError('Unknown generation role: '+role)
|
||||
data_dir=Path(data_dir); resources=vocabulary.load(data_dir/'composition')
|
||||
settings=settings or {}
|
||||
from generator.solo.policy import DEFAULT
|
||||
algorithm=VERSION+'/'+DEFAULT.composition_version if role.endswith('_solo') else VERSION
|
||||
registered=repertoire.lookup(source,role.removesuffix('_solo'),data_dir) if role.endswith('_solo') and not settings.get('predict',False) else None
|
||||
algorithm=VERSION+'/'+learned.VERSION if role.endswith('_solo') else VERSION
|
||||
inputs={'version':algorithm,'source':source.musical_key,'role':role,'settings':settings,
|
||||
'vocabulary':dependencies(role,resources),'catalogs':_resource_key(data_dir,role),'code':_code_key(role)}
|
||||
'vocabulary':dependencies(role,resources),'catalogs':({'authored':registered['sha256'],'id':registered['id'],'pitch_convention':registered['pitch_convention']} if registered else _resource_key(data_dir,role)),'code':_code_key(role)}
|
||||
# Source notation/lyrics also invalidate cached score representation, never the musical seed.
|
||||
key=vocabulary.fingerprint({'inputs':inputs,'notation':source.document_json})
|
||||
path=Path(cache_dir)/role/(key+'.json') if cache_dir else None
|
||||
@@ -185,7 +184,7 @@ def _generate_role(source, role, data_dir, settings=None, cache_dir=None):
|
||||
with vocabulary.using(resources,role,settings|{'choice_seed':seed}):
|
||||
if role.endswith('_solo'):
|
||||
result=generate._arrange_document(document,part,{},owned={'slash'})
|
||||
from generator.solo.arrangement import regions,annotate
|
||||
from generator.solo.arrangement import regions
|
||||
si=3 if part=='acoustic' else 4
|
||||
first,count,offset,length=regions(document)[si]
|
||||
# The template/harmony comes directly from source form, not another generated part.
|
||||
@@ -202,17 +201,20 @@ def _generate_role(source, role, data_dir, settings=None, cache_dir=None):
|
||||
template.insert(0,copy.deepcopy(result['staves'][0]['measures'][first-1]))
|
||||
notation_first-=1
|
||||
initial=accompaniment.carried_chords(result['staves'][0]['measures'])[first-1]
|
||||
scale_library,sequence_library,rhythm_library=_solo_libraries(data_dir,tuple(sorted(inputs['catalogs'].items())),cache_dir)
|
||||
solo=composer.compose(analysis.analyze(document),template,part,end,
|
||||
initial=initial,scales=scale_library,sequences=sequence_library,rhythms=rhythm_library,
|
||||
expressions_enabled=vocabulary.role().get('expressions',True),start=start)
|
||||
if registered:
|
||||
solo=repertoire.realize(registered,analysis.analyze(document),template,part,start,end,initial)
|
||||
else:
|
||||
solo_model=_solo_model(str(data_dir),inputs['catalogs']['model'])
|
||||
solo=composer.compose(analysis.analyze(document),template,part,end,
|
||||
initial=initial,learned_model=solo_model,
|
||||
expressions_enabled=vocabulary.role().get('expressions',True),start=start)
|
||||
composition_timings={k:v for k,v in solo.diagnostics.items() if k.endswith('_seconds')}
|
||||
relative=tuple(dataclasses.replace(n,start=n.start-start,end=n.end-start) for n in solo.notes())
|
||||
output=notation.measures(result['staves'][0]['measures'][notation_first:first+count],relative,'solo_'+part)
|
||||
notation.replace_region(result,notation_first,output);annotate(result,solo,offset)
|
||||
notation.replace_region(result,notation_first,output)
|
||||
details=report.structure(solo)
|
||||
details['allocation']={'start':str(start),'end':str(end),'lead_in_measures':first-notation_first}
|
||||
solo_report=report.describe(solo,scale_library,first,offset,detail=details)
|
||||
solo_report=report.describe(solo,first,offset,detail=details)
|
||||
elif role.endswith('_accompaniment'):
|
||||
canonical=comping.Canonical(document,part,seed)
|
||||
result=generate._arrange_document(document,part,{},owned={'accompaniment'},accompaniment=canonical)
|
||||
|
||||
Reference in New Issue
Block a user