Refactored namespaces
This commit is contained in:
@@ -29,39 +29,40 @@
|
||||
#include "database/Session.hpp"
|
||||
#include "database/Track.hpp"
|
||||
#include "services/cover/ICoverService.hpp"
|
||||
#include "utils/IConfig.hpp"
|
||||
#include "utils/ILogger.hpp"
|
||||
#include "utils/Service.hpp"
|
||||
#include "utils/StreamLogger.hpp"
|
||||
#include "core/IConfig.hpp"
|
||||
#include "core/ILogger.hpp"
|
||||
#include "core/Service.hpp"
|
||||
#include "core/StreamLogger.hpp"
|
||||
|
||||
static
|
||||
void
|
||||
dumpTrackCovers(Database::Session& session, Image::ImageSize width)
|
||||
namespace lms
|
||||
{
|
||||
using namespace Database;
|
||||
|
||||
RangeResults<Database::TrackId> trackIds;
|
||||
void dumpTrackCovers(db::Session& session, image::ImageSize width)
|
||||
{
|
||||
auto transaction{ session.createReadTransaction() };
|
||||
trackIds = Database::Track::findIds(session, Database::Track::FindParameters{});
|
||||
}
|
||||
using namespace db;
|
||||
|
||||
for (const Database::TrackId trackId : trackIds.results)
|
||||
{
|
||||
std::cout << "Getting cover for track id " << trackId.toString() << std::endl;
|
||||
Service<Cover::ICoverService>::get()->getFromTrack(trackId, width);
|
||||
RangeResults<db::TrackId> trackIds;
|
||||
{
|
||||
auto transaction{ session.createReadTransaction() };
|
||||
trackIds = db::Track::findIds(session, db::Track::FindParameters{});
|
||||
}
|
||||
|
||||
for (const db::TrackId trackId : trackIds.results)
|
||||
{
|
||||
std::cout << "Getting cover for track id " << trackId.toString() << std::endl;
|
||||
core::Service<cover::ICoverService>::get()->getFromTrack(trackId, width);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
try
|
||||
{
|
||||
using namespace lms;
|
||||
namespace po = boost::program_options;
|
||||
|
||||
// log to stdout
|
||||
Service<ILogger> logger{ std::make_unique<StreamLogger>(std::cout) };
|
||||
core::Service<core::logging::ILogger> logger{ std::make_unique<core::logging::StreamLogger>(std::cout) };
|
||||
|
||||
po::options_description desc{ "Allowed options" };
|
||||
desc.add_options()
|
||||
@@ -82,13 +83,13 @@ int main(int argc, char* argv[])
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
Service<IConfig> config{ createConfig(vm["conf"].as<std::string>()) };
|
||||
Database::Db db{ config->getPath("working-dir") / "lms.db" };
|
||||
Service<Cover::ICoverService> coverArtService{ Cover::createCoverService(db, argv[0], vm["default-cover"].as<std::string>()) };
|
||||
core::Service<core::IConfig> config{ core::createConfig(vm["conf"].as<std::string>()) };
|
||||
db::Db db{ config->getPath("working-dir") / "lms.db" };
|
||||
core::Service<cover::ICoverService> coverArtService{ cover::createCoverService(db, argv[0], vm["default-cover"].as<std::string>()) };
|
||||
|
||||
coverArtService->setJpegQuality(config->getULong("cover-jpeg-quality", vm["quality"].as<unsigned>()));
|
||||
|
||||
Database::Session session{ db };
|
||||
db::Session session{ db };
|
||||
|
||||
if (vm.count("tracks"))
|
||||
dumpTrackCovers(session, vm["size"].as<unsigned>());
|
||||
|
||||
@@ -5,6 +5,6 @@ add_executable(lms-db-generator
|
||||
|
||||
target_link_libraries(lms-db-generator PRIVATE
|
||||
lmsdatabase
|
||||
lmsutils
|
||||
lmscore
|
||||
Boost::program_options
|
||||
)
|
||||
|
||||
@@ -37,12 +37,12 @@
|
||||
#include "database/Track.hpp"
|
||||
#include "database/TrackArtistLink.hpp"
|
||||
|
||||
#include "utils/IConfig.hpp"
|
||||
#include "utils/Random.hpp"
|
||||
#include "utils/StreamLogger.hpp"
|
||||
#include "utils/Service.hpp"
|
||||
#include "core/IConfig.hpp"
|
||||
#include "core/Random.hpp"
|
||||
#include "core/StreamLogger.hpp"
|
||||
#include "core/Service.hpp"
|
||||
|
||||
namespace
|
||||
namespace lms
|
||||
{
|
||||
struct GeneratorParameters
|
||||
{
|
||||
@@ -60,32 +60,32 @@ namespace
|
||||
|
||||
struct GenerationContext
|
||||
{
|
||||
Database::Session& session;
|
||||
std::vector<Database::MediaLibrary::pointer> mediaLibraries;
|
||||
std::vector<Database::Cluster::pointer> genres;
|
||||
std::vector<Database::Cluster::pointer> moods;
|
||||
GenerationContext(Database::Session& _session) : session{ _session } {}
|
||||
db::Session& session;
|
||||
std::vector<db::MediaLibrary::pointer> mediaLibraries;
|
||||
std::vector<db::Cluster::pointer> genres;
|
||||
std::vector<db::Cluster::pointer> moods;
|
||||
GenerationContext(db::Session& _session) : session{ _session } {}
|
||||
};
|
||||
|
||||
Database::Cluster::pointer generateCluster(Database::Session& session, Database::ClusterType::pointer clusterType)
|
||||
db::Cluster::pointer generateCluster(db::Session& session, db::ClusterType::pointer clusterType)
|
||||
{
|
||||
const std::string clusterName{ std::string{ clusterType->getName() } + "-" + std::string{ UUID::generate().getAsString() } };
|
||||
return session.create<Database::Cluster>(clusterType, clusterName);
|
||||
const std::string clusterName{ std::string{ clusterType->getName() } + "-" + std::string{ core::UUID::generate().getAsString() } };
|
||||
return session.create<db::Cluster>(clusterType, clusterName);
|
||||
}
|
||||
|
||||
Database::Artist::pointer generateArtist(Database::Session& session)
|
||||
db::Artist::pointer generateArtist(db::Session& session)
|
||||
{
|
||||
const UUID artistMBID{ UUID::generate() };
|
||||
const std::string artistName{ "Artist-" + std::string{ UUID::generate().getAsString() } };
|
||||
return session.create<Database::Artist>(artistName, artistMBID);
|
||||
const core::UUID artistMBID{ core::UUID::generate() };
|
||||
const std::string artistName{ "Artist-" + std::string{ core::UUID::generate().getAsString() } };
|
||||
return session.create<db::Artist>(artistName, artistMBID);
|
||||
}
|
||||
|
||||
void generateRelease(const GeneratorParameters& params, GenerationContext& context)
|
||||
{
|
||||
using namespace Database;
|
||||
using namespace db;
|
||||
|
||||
const UUID releaseMBID{ UUID::generate() };
|
||||
const std::string releaseName{ "Release-" + std::string{ UUID::generate().getAsString() } };
|
||||
const core::UUID releaseMBID{ core::UUID::generate() };
|
||||
const std::string releaseName{ "Release-" + std::string{ core::UUID::generate().getAsString() } };
|
||||
Release::pointer release{ context.session.create<Release>(releaseName, releaseMBID) };
|
||||
|
||||
Artist::pointer artist{ generateArtist(context.session) };
|
||||
@@ -94,25 +94,25 @@ namespace
|
||||
{
|
||||
Track::pointer track{ context.session.create<Track>(params.trackPath) };
|
||||
|
||||
track.modify()->setName("Track-" + std::string{ UUID::generate().getAsString() });
|
||||
track.modify()->setName("Track-" + std::string{ core::UUID::generate().getAsString() });
|
||||
track.modify()->setDiscNumber(1);
|
||||
track.modify()->setTrackNumber(i);
|
||||
track.modify()->setDuration(std::chrono::seconds{ Random::getRandom(30, 300) });
|
||||
track.modify()->setDuration(std::chrono::seconds{ core::random::getRandom(30, 300) });
|
||||
track.modify()->setRelease(release);
|
||||
track.modify()->setTrackMBID(UUID::generate());
|
||||
track.modify()->setRecordingMBID(UUID::generate());
|
||||
track.modify()->setTrackMBID(core::UUID::generate());
|
||||
track.modify()->setRecordingMBID(core::UUID::generate());
|
||||
track.modify()->setTotalTrack(params.trackCountPerRelease);
|
||||
if (!context.mediaLibraries.empty())
|
||||
track.modify()->setMediaLibrary(*Random::pickRandom(context.mediaLibraries));
|
||||
track.modify()->setMediaLibrary(*core::random::pickRandom(context.mediaLibraries));
|
||||
|
||||
TrackArtistLink::create(context.session, track, artist, TrackArtistLinkType::Artist);
|
||||
TrackArtistLink::create(context.session, track, artist, TrackArtistLinkType::ReleaseArtist);
|
||||
|
||||
std::vector<ObjectPtr<Cluster>> clusters;
|
||||
if (!context.genres.empty())
|
||||
clusters.push_back(*Random::pickRandom(context.genres));
|
||||
clusters.push_back(*core::random::pickRandom(context.genres));
|
||||
if (!context.moods.empty())
|
||||
clusters.push_back(*Random::pickRandom(context.moods));
|
||||
clusters.push_back(*core::random::pickRandom(context.moods));
|
||||
track.modify()->setClusters(clusters);
|
||||
}
|
||||
}
|
||||
@@ -137,22 +137,22 @@ namespace
|
||||
|
||||
// create some random media libraries
|
||||
for (std::size_t i{}; i < params.mediaLibraryCount; ++i)
|
||||
context.mediaLibraries.push_back(context.session.create<Database::MediaLibrary>());
|
||||
context.mediaLibraries.push_back(context.session.create<db::MediaLibrary>());
|
||||
|
||||
// create some random genres/moods
|
||||
{
|
||||
Database::ClusterType::pointer genre{ Database::ClusterType::find(context.session, "GENRE") };
|
||||
db::ClusterType::pointer genre{ db::ClusterType::find(context.session, "GENRE") };
|
||||
if (!genre)
|
||||
genre = context.session.create<Database::ClusterType>("GENRE");
|
||||
genre = context.session.create<db::ClusterType>("GENRE");
|
||||
|
||||
for (std::size_t i{}; i < params.genreCount; ++i)
|
||||
context.genres.push_back(generateCluster(context.session, genre));
|
||||
}
|
||||
|
||||
{
|
||||
Database::ClusterType::pointer mood{ Database::ClusterType::find(context.session, "MOOD") };
|
||||
db::ClusterType::pointer mood{ db::ClusterType::find(context.session, "MOOD") };
|
||||
if (!mood)
|
||||
mood = context.session.create<Database::ClusterType>("MOOD");
|
||||
mood = context.session.create<db::ClusterType>("MOOD");
|
||||
|
||||
for (std::size_t i{}; i < params.moodCount; ++i)
|
||||
context.moods.push_back(generateCluster(context.session, mood));
|
||||
@@ -164,31 +164,32 @@ int main(int argc, char* argv[])
|
||||
{
|
||||
try
|
||||
{
|
||||
// log to stdout
|
||||
Service<ILogger> logger{ std::make_unique<StreamLogger>(std::cout) };
|
||||
using namespace lms;
|
||||
namespace program_options = boost::program_options;
|
||||
|
||||
namespace po = boost::program_options;
|
||||
// log to stdout
|
||||
core::Service<core::logging::ILogger> logger{ std::make_unique<core::logging::StreamLogger>(std::cout) };
|
||||
|
||||
const GeneratorParameters defaultParams;
|
||||
|
||||
po::options_description options{ "Options" };
|
||||
program_options::options_description options{ "Options" };
|
||||
options.add_options()
|
||||
("conf,c", po::value<std::string>()->default_value("/etc/lms.conf"), "lms config file")
|
||||
("media-library-count", po::value<unsigned>()->default_value(defaultParams.mediaLibraryCount), "Number of media libraries to use")
|
||||
("release-count-per-batch", po::value<unsigned>()->default_value(defaultParams.releaseCountPerBatch), "Number of releases to generate before committing transaction")
|
||||
("release-count", po::value<unsigned>()->default_value(defaultParams.releaseCount), "Number of releases to generate")
|
||||
("track-count-per-release", po::value<unsigned>()->default_value(defaultParams.trackCountPerRelease), "Number of tracks per release")
|
||||
("compilation-ratio", po::value<float>()->default_value(defaultParams.compilationRatio), "Compilation ratio (compilation means all tracks have a different artist)")
|
||||
("track-path", po::value<std::string>()->required(), "Path of a valid track file, that will be used for all generated tracks")
|
||||
("genre-count", po::value<unsigned>()->default_value(defaultParams.genreCount), "Number of genres to generate")
|
||||
("genre-count-per-track", po::value<unsigned>()->default_value(defaultParams.genreCountPerTrack), "Number of genres to assign to each track")
|
||||
("mood-count", po::value<unsigned>()->default_value(defaultParams.moodCount), "Number of moods to generate")
|
||||
("mood-count-per-track", po::value<unsigned>()->default_value(defaultParams.moodCountPerTrack), "Number of moods to assign to each track")
|
||||
("conf,c", program_options::value<std::string>()->default_value("/etc/lms.conf"), "lms config file")
|
||||
("media-library-count", program_options::value<unsigned>()->default_value(defaultParams.mediaLibraryCount), "Number of media libraries to use")
|
||||
("release-count-per-batch", program_options::value<unsigned>()->default_value(defaultParams.releaseCountPerBatch), "Number of releases to generate before committing transaction")
|
||||
("release-count", program_options::value<unsigned>()->default_value(defaultParams.releaseCount), "Number of releases to generate")
|
||||
("track-count-per-release", program_options::value<unsigned>()->default_value(defaultParams.trackCountPerRelease), "Number of tracks per release")
|
||||
("compilation-ratio", program_options::value<float>()->default_value(defaultParams.compilationRatio), "Compilation ratio (compilation means all tracks have a different artist)")
|
||||
("track-path", program_options::value<std::string>()->required(), "Path of a valid track file, that will be used for all generated tracks")
|
||||
("genre-count", program_options::value<unsigned>()->default_value(defaultParams.genreCount), "Number of genres to generate")
|
||||
("genre-count-per-track", program_options::value<unsigned>()->default_value(defaultParams.genreCountPerTrack), "Number of genres to assign to each track")
|
||||
("mood-count", program_options::value<unsigned>()->default_value(defaultParams.moodCount), "Number of moods to generate")
|
||||
("mood-count-per-track", program_options::value<unsigned>()->default_value(defaultParams.moodCountPerTrack), "Number of moods to assign to each track")
|
||||
("help,h", "produce help message")
|
||||
;
|
||||
|
||||
po::variables_map vm;
|
||||
po::store(po::parse_command_line(argc, argv, options), vm);
|
||||
program_options::variables_map vm;
|
||||
program_options::store(program_options::parse_command_line(argc, argv, options), vm);
|
||||
|
||||
if (vm.count("help")) {
|
||||
std::cout << options << "\n";
|
||||
@@ -196,7 +197,7 @@ int main(int argc, char* argv[])
|
||||
}
|
||||
|
||||
// notify required params
|
||||
po::notify(vm);
|
||||
program_options::notify(vm);
|
||||
|
||||
GeneratorParameters genParams;
|
||||
genParams.mediaLibraryCount = vm["media-library-count"].as<unsigned>();
|
||||
@@ -209,9 +210,9 @@ int main(int argc, char* argv[])
|
||||
if (!std::filesystem::exists(genParams.trackPath))
|
||||
throw std::runtime_error{ "File '" + genParams.trackPath.string() + "' does not exist!" };
|
||||
|
||||
Service<IConfig> config{ createConfig(vm["conf"].as<std::string>()) };
|
||||
Database::Db db{ config->getPath("working-dir") / "lms.db" };
|
||||
Database::Session session{ db };
|
||||
core::Service<core::IConfig> config{ core::createConfig(vm["conf"].as<std::string>()) };
|
||||
db::Db db{ config->getPath("working-dir") / "lms.db" };
|
||||
db::Session session{ db };
|
||||
std::cout << "Starting generation..." << std::endl;
|
||||
|
||||
GenerationContext genContext{ session };
|
||||
|
||||
@@ -5,7 +5,7 @@ add_executable(lms-metadata
|
||||
|
||||
target_link_libraries(lms-metadata PRIVATE
|
||||
lmsmetadata
|
||||
lmsutils
|
||||
lmscore
|
||||
)
|
||||
|
||||
install(TARGETS lms-metadata DESTINATION bin)
|
||||
|
||||
@@ -28,11 +28,11 @@
|
||||
|
||||
#include "metadata/Exception.hpp"
|
||||
#include "metadata/IParser.hpp"
|
||||
#include "utils/StreamLogger.hpp"
|
||||
#include "core/StreamLogger.hpp"
|
||||
|
||||
namespace
|
||||
namespace lms::metadata
|
||||
{
|
||||
std::ostream& operator<<(std::ostream& os, const MetaData::Artist& artist)
|
||||
std::ostream& operator<<(std::ostream& os, const Artist& artist)
|
||||
{
|
||||
os << artist.name;
|
||||
|
||||
@@ -45,7 +45,7 @@ namespace
|
||||
return os;
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const MetaData::Release& release)
|
||||
std::ostream& operator<<(std::ostream& os, const Release& release)
|
||||
{
|
||||
os << release.name;
|
||||
if (!release.sortName.empty())
|
||||
@@ -64,7 +64,7 @@ namespace
|
||||
if (!release.artistDisplayName.empty())
|
||||
std::cout << "\tDisplay artist: " << release.artistDisplayName << std::endl;
|
||||
|
||||
for (const MetaData::Artist& artist : release.artists)
|
||||
for (const Artist& artist : release.artists)
|
||||
std::cout << "\tRelease artist: " << artist << std::endl;
|
||||
|
||||
for (std::string_view releaseType : release.releaseTypes)
|
||||
@@ -73,7 +73,7 @@ namespace
|
||||
return os;
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const MetaData::Medium& medium)
|
||||
std::ostream& operator<<(std::ostream& os, const Medium& medium)
|
||||
{
|
||||
if (!medium.name.empty())
|
||||
os << medium.name;
|
||||
@@ -97,9 +97,9 @@ namespace
|
||||
return os;
|
||||
}
|
||||
|
||||
void parse(MetaData::IParser& parser, const std::filesystem::path& file)
|
||||
void parse(IParser& parser, const std::filesystem::path& file)
|
||||
{
|
||||
using namespace MetaData;
|
||||
using namespace metadata;
|
||||
|
||||
const auto start{ std::chrono::steady_clock::now() };
|
||||
std::unique_ptr<Track> track{ parser.parse(file, true) };
|
||||
@@ -222,8 +222,10 @@ int main(int argc, char* argv[])
|
||||
|
||||
try
|
||||
{
|
||||
using namespace lms;
|
||||
|
||||
// log to stdout
|
||||
Service<ILogger> logger{ std::make_unique<StreamLogger>(std::cout, StreamLogger::allSeverities) };
|
||||
core::Service<core::logging::ILogger> logger{ std::make_unique<core::logging::StreamLogger>(std::cout, core::logging::StreamLogger::allSeverities) };
|
||||
|
||||
for (std::size_t i{}; i < static_cast<std::size_t>(argc - 1); ++i)
|
||||
{
|
||||
@@ -234,10 +236,10 @@ int main(int argc, char* argv[])
|
||||
try
|
||||
{
|
||||
std::cout << "Using av:" << std::endl;
|
||||
auto parser{ MetaData::createParser(MetaData::ParserBackend::AvFormat, MetaData::ParserReadStyle::Accurate) };
|
||||
auto parser{ metadata::createParser(metadata::ParserBackend::AvFormat, metadata::ParserReadStyle::Accurate) };
|
||||
parse(*parser, file);
|
||||
}
|
||||
catch (MetaData::Exception& e)
|
||||
catch (metadata::Exception& e)
|
||||
{
|
||||
std::cerr << "Parsing failed: " << e.what() << std::endl;
|
||||
}
|
||||
@@ -245,10 +247,10 @@ int main(int argc, char* argv[])
|
||||
try
|
||||
{
|
||||
std::cout << "Using TagLib:" << std::endl;
|
||||
auto parser{ MetaData::createParser(MetaData::ParserBackend::TagLib, MetaData::ParserReadStyle::Accurate) };
|
||||
auto parser{ metadata::createParser(metadata::ParserBackend::TagLib, metadata::ParserReadStyle::Accurate) };
|
||||
parse(*parser, file);
|
||||
}
|
||||
catch (MetaData::Exception& e)
|
||||
catch (metadata::Exception& e)
|
||||
{
|
||||
std::cerr << "Parsing failed: " << e.what() << std::endl;
|
||||
}
|
||||
|
||||
@@ -32,94 +32,97 @@
|
||||
#include "database/Track.hpp"
|
||||
#include "database/Types.hpp"
|
||||
#include "services/recommendation/IRecommendationService.hpp"
|
||||
#include "utils/IConfig.hpp"
|
||||
#include "utils/Service.hpp"
|
||||
#include "utils/StreamLogger.hpp"
|
||||
#include "core/IConfig.hpp"
|
||||
#include "core/Service.hpp"
|
||||
#include "core/StreamLogger.hpp"
|
||||
|
||||
using namespace Database;
|
||||
|
||||
static void dumpTracksRecommendation(Session session, Recommendation::IRecommendationService& recommendationService, unsigned maxSimilarityCount)
|
||||
namespace lms
|
||||
{
|
||||
const RangeResults<TrackId> trackIds{ [&]
|
||||
{
|
||||
auto transaction {session.createReadTransaction()};
|
||||
return Track::findIds(session, Track::FindParameters{});
|
||||
}() };
|
||||
using namespace db;
|
||||
|
||||
std::cout << "*** Tracks (" << trackIds.results.size() << ") ***" << std::endl;
|
||||
for (const TrackId trackId : trackIds.results)
|
||||
void dumpTracksRecommendation(Session session, recommendation::IRecommendationService& recommendationService, unsigned maxSimilarityCount)
|
||||
{
|
||||
auto trackToString = [&](const TrackId trackId)
|
||||
const RangeResults<TrackId> trackIds{ [&]
|
||||
{
|
||||
std::string res;
|
||||
auto transaction{ session.createReadTransaction() };
|
||||
const Track::pointer track{ Track::find(session, trackId) };
|
||||
auto transaction {session.createReadTransaction()};
|
||||
return Track::findIds(session, Track::FindParameters{});
|
||||
}() };
|
||||
|
||||
res += track->getName();
|
||||
if (track->getRelease())
|
||||
res += " [" + std::string{ track->getRelease()->getName() } + "]";
|
||||
for (auto artist : track->getArtists({ TrackArtistLinkType::Artist }))
|
||||
res += " - " + artist->getName();
|
||||
for (auto cluster : track->getClusters())
|
||||
res += " {" + std::string{ cluster->getType()->getName() } + "-" + std::string{ cluster->getName() } + "}";
|
||||
std::cout << "*** Tracks (" << trackIds.results.size() << ") ***" << std::endl;
|
||||
for (const TrackId trackId : trackIds.results)
|
||||
{
|
||||
auto trackToString = [&](const TrackId trackId)
|
||||
{
|
||||
std::string res;
|
||||
auto transaction{ session.createReadTransaction() };
|
||||
const Track::pointer track{ Track::find(session, trackId) };
|
||||
|
||||
return res;
|
||||
};
|
||||
res += track->getName();
|
||||
if (track->getRelease())
|
||||
res += " [" + std::string{ track->getRelease()->getName() } + "]";
|
||||
for (auto artist : track->getArtists({ TrackArtistLinkType::Artist }))
|
||||
res += " - " + artist->getName();
|
||||
for (auto cluster : track->getClusters())
|
||||
res += " {" + std::string{ cluster->getType()->getName() } + "-" + std::string{ cluster->getName() } + "}";
|
||||
|
||||
std::cout << "Processing track '" << trackToString(trackId) << std::endl;
|
||||
for (TrackId similarTrackId : recommendationService.findSimilarTracks({ trackId }, maxSimilarityCount))
|
||||
std::cout << "\t- Similar track '" << trackToString(similarTrackId) << std::endl;
|
||||
return res;
|
||||
};
|
||||
|
||||
std::cout << "Processing track '" << trackToString(trackId) << std::endl;
|
||||
for (TrackId similarTrackId : recommendationService.findSimilarTracks({ trackId }, maxSimilarityCount))
|
||||
std::cout << "\t- Similar track '" << trackToString(similarTrackId) << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void dumpReleasesRecommendation(Session session, Recommendation::IRecommendationService& recommendationService, unsigned maxSimilarityCount)
|
||||
{
|
||||
const RangeResults<ReleaseId> releaseIds{ std::invoke([&]
|
||||
{
|
||||
auto transaction {session.createReadTransaction()};
|
||||
return Release::findIds(session, Release::FindParameters {});
|
||||
}) };
|
||||
|
||||
std::cout << "*** Releases ***" << std::endl;
|
||||
for (const ReleaseId releaseId : releaseIds.results)
|
||||
void dumpReleasesRecommendation(Session session, recommendation::IRecommendationService& recommendationService, unsigned maxSimilarityCount)
|
||||
{
|
||||
auto releaseToString = [&](ReleaseId releaseId) -> std::string
|
||||
const RangeResults<ReleaseId> releaseIds{ std::invoke([&]
|
||||
{
|
||||
auto transaction{ session.createReadTransaction() };
|
||||
auto transaction {session.createReadTransaction()};
|
||||
return Release::findIds(session, Release::FindParameters {});
|
||||
}) };
|
||||
|
||||
Release::pointer release{ Release::find(session, releaseId) };
|
||||
return std::string{ release->getName() };
|
||||
};
|
||||
std::cout << "*** Releases ***" << std::endl;
|
||||
for (const ReleaseId releaseId : releaseIds.results)
|
||||
{
|
||||
auto releaseToString = [&](ReleaseId releaseId) -> std::string
|
||||
{
|
||||
auto transaction{ session.createReadTransaction() };
|
||||
|
||||
std::cout << "Processing release '" << releaseToString(releaseId) << "'" << std::endl;
|
||||
for (const ReleaseId similarReleaseId : recommendationService.getSimilarReleases(releaseId, maxSimilarityCount))
|
||||
std::cout << "\t- Similar release '" << releaseToString(similarReleaseId) << "'" << std::endl;
|
||||
Release::pointer release{ Release::find(session, releaseId) };
|
||||
return std::string{ release->getName() };
|
||||
};
|
||||
|
||||
std::cout << "Processing release '" << releaseToString(releaseId) << "'" << std::endl;
|
||||
for (const ReleaseId similarReleaseId : recommendationService.getSimilarReleases(releaseId, maxSimilarityCount))
|
||||
std::cout << "\t- Similar release '" << releaseToString(similarReleaseId) << "'" << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void dumpArtistsRecommendation(Session session, Recommendation::IRecommendationService& recommendationService, unsigned maxSimilarityCount)
|
||||
{
|
||||
const RangeResults<ArtistId> artistIds = std::invoke([&]()
|
||||
{
|
||||
auto transaction{ session.createReadTransaction() };
|
||||
return Artist::findIds(session, Artist::FindParameters{});
|
||||
});
|
||||
|
||||
std::cout << "*** Artists ***" << std::endl;
|
||||
for (ArtistId artistId : artistIds.results)
|
||||
void dumpArtistsRecommendation(Session session, recommendation::IRecommendationService& recommendationService, unsigned maxSimilarityCount)
|
||||
{
|
||||
auto artistToString = [&](ArtistId artistId)
|
||||
const RangeResults<ArtistId> artistIds = std::invoke([&]()
|
||||
{
|
||||
auto transaction{ session.createReadTransaction() };
|
||||
return Artist::findIds(session, Artist::FindParameters{});
|
||||
});
|
||||
|
||||
Artist::pointer artist{ Artist::find(session, artistId) };
|
||||
return artist->getName();
|
||||
};
|
||||
|
||||
std::cout << "Processing artist '" << artistToString(artistId) << "'" << std::endl;
|
||||
for (ArtistId similarArtistId : recommendationService.getSimilarArtists(artistId, { TrackArtistLinkType::Artist, TrackArtistLinkType::ReleaseArtist }, maxSimilarityCount))
|
||||
std::cout << "*** Artists ***" << std::endl;
|
||||
for (ArtistId artistId : artistIds.results)
|
||||
{
|
||||
std::cout << "\t- Similar artist '" << artistToString(similarArtistId) << "'" << std::endl;
|
||||
auto artistToString = [&](ArtistId artistId)
|
||||
{
|
||||
auto transaction{ session.createReadTransaction() };
|
||||
|
||||
Artist::pointer artist{ Artist::find(session, artistId) };
|
||||
return artist->getName();
|
||||
};
|
||||
|
||||
std::cout << "Processing artist '" << artistToString(artistId) << "'" << std::endl;
|
||||
for (ArtistId similarArtistId : recommendationService.getSimilarArtists(artistId, { TrackArtistLinkType::Artist, TrackArtistLinkType::ReleaseArtist }, maxSimilarityCount))
|
||||
{
|
||||
std::cout << "\t- Similar artist '" << artistToString(similarArtistId) << "'" << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -128,10 +131,11 @@ int main(int argc, char* argv[])
|
||||
{
|
||||
try
|
||||
{
|
||||
using namespace lms;
|
||||
namespace po = boost::program_options;
|
||||
|
||||
// log to stdout
|
||||
Service<ILogger> logger{ std::make_unique<StreamLogger>(std::cout) };
|
||||
core::Service<core::logging::ILogger> logger{ std::make_unique<core::logging::StreamLogger>(std::cout) };
|
||||
|
||||
po::options_description desc{ "Allowed options" };
|
||||
desc.add_options()
|
||||
@@ -152,13 +156,13 @@ int main(int argc, char* argv[])
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
Service<IConfig> config{ createConfig(vm["conf"].as<std::string>()) };
|
||||
core::Service<core::IConfig> config{ core::createConfig(vm["conf"].as<std::string>()) };
|
||||
|
||||
Db db{ config->getPath("working-dir") / "lms.db" };
|
||||
Session session{ db };
|
||||
|
||||
std::cout << "Creating recommendation service..." << std::endl;
|
||||
const auto recommendationService{ Recommendation::createRecommendationService(db) };
|
||||
const auto recommendationService{ recommendation::createRecommendationService(db) };
|
||||
std::cout << "Recommendation service created!" << std::endl;
|
||||
|
||||
std::cout << "Loading recommendation service..." << std::endl;
|
||||
|
||||
@@ -19,48 +19,48 @@
|
||||
|
||||
#include <numeric>
|
||||
|
||||
#include "utils/Random.hpp"
|
||||
#include "core/Random.hpp"
|
||||
|
||||
#include "ParallelFor.hpp"
|
||||
|
||||
template<typename Individual>
|
||||
class GeneticAlgorithm
|
||||
{
|
||||
public:
|
||||
using Score = float;
|
||||
public:
|
||||
using Score = float;
|
||||
|
||||
using BreedFunction = std::function<Individual(const Individual&, const Individual&)>;
|
||||
using MutateFunction = std::function<void(Individual&)>;
|
||||
using ScoreFunction = std::function<Score(const Individual&)>;
|
||||
using BreedFunction = std::function<Individual(const Individual&, const Individual&)>;
|
||||
using MutateFunction = std::function<void(Individual&)>;
|
||||
using ScoreFunction = std::function<Score(const Individual&)>;
|
||||
|
||||
struct Params
|
||||
{
|
||||
std::size_t nbWorkers {1};
|
||||
std::size_t nbGenerations;
|
||||
float crossoverRatio {0.5};
|
||||
float mutationProbability {0.05};
|
||||
BreedFunction breedFunction;
|
||||
MutateFunction mutateFunction;
|
||||
ScoreFunction scoreFunction;
|
||||
};
|
||||
struct Params
|
||||
{
|
||||
std::size_t nbWorkers {1};
|
||||
std::size_t nbGenerations;
|
||||
float crossoverRatio {0.5};
|
||||
float mutationProbability {0.05};
|
||||
BreedFunction breedFunction;
|
||||
MutateFunction mutateFunction;
|
||||
ScoreFunction scoreFunction;
|
||||
};
|
||||
|
||||
GeneticAlgorithm(const Params& params);
|
||||
GeneticAlgorithm(const Params& params);
|
||||
|
||||
// Returns the individual that has the maximum score after processing the requested generations
|
||||
Individual simulate(const std::vector<Individual>& initialPopulation);
|
||||
// Returns the individual that has the maximum score after processing the requested generations
|
||||
Individual simulate(const std::vector<Individual>& initialPopulation);
|
||||
|
||||
private:
|
||||
struct ScoredIndividual
|
||||
{
|
||||
Individual individual;
|
||||
std::optional<Score> score {};
|
||||
};
|
||||
private:
|
||||
struct ScoredIndividual
|
||||
{
|
||||
Individual individual;
|
||||
std::optional<Score> score {};
|
||||
};
|
||||
|
||||
void scoreAndSortPopulation(std::vector<ScoredIndividual>& population);
|
||||
Score getTotalScore(const std::vector<ScoredIndividual>& population) const;
|
||||
typename std::vector<ScoredIndividual>::const_iterator pickRandomRouletteWheel(const std::vector<ScoredIndividual>& population, Score totalScore);
|
||||
void scoreAndSortPopulation(std::vector<ScoredIndividual>& population);
|
||||
Score getTotalScore(const std::vector<ScoredIndividual>& population) const;
|
||||
typename std::vector<ScoredIndividual>::const_iterator pickRandomRouletteWheel(const std::vector<ScoredIndividual>& population, Score totalScore);
|
||||
|
||||
Params _params;
|
||||
Params _params;
|
||||
};
|
||||
|
||||
template<typename Individual>
|
||||
@@ -74,60 +74,60 @@ template<typename Individual>
|
||||
Individual
|
||||
GeneticAlgorithm<Individual>::simulate(const std::vector<Individual>& initialPopulation)
|
||||
{
|
||||
const std::size_t childrenCountPerGeneration {static_cast<std::size_t>(initialPopulation.size() * _params.crossoverRatio)};
|
||||
if (initialPopulation.size() < 10)
|
||||
throw std::runtime_error("Initial population must has at least 10 elements");
|
||||
const std::size_t childrenCountPerGeneration {static_cast<std::size_t>(initialPopulation.size() * _params.crossoverRatio)};
|
||||
if (initialPopulation.size() < 10)
|
||||
throw std::runtime_error("Initial population must has at least 10 elements");
|
||||
|
||||
std::vector<ScoredIndividual> scoredPopulation;
|
||||
scoredPopulation.reserve(initialPopulation.size());
|
||||
std::vector<ScoredIndividual> scoredPopulation;
|
||||
scoredPopulation.reserve(initialPopulation.size());
|
||||
|
||||
std::transform(std::cbegin(initialPopulation), std::cend(initialPopulation), std::back_inserter(scoredPopulation ),
|
||||
[](const Individual& individual) { return ScoredIndividual {individual};});
|
||||
std::transform(std::cbegin(initialPopulation), std::cend(initialPopulation), std::back_inserter(scoredPopulation ),
|
||||
[](const Individual& individual) { return ScoredIndividual {individual};});
|
||||
|
||||
scoreAndSortPopulation(scoredPopulation);
|
||||
scoreAndSortPopulation(scoredPopulation);
|
||||
|
||||
for (std::size_t currentGeneration {}; currentGeneration < _params.nbGenerations; ++currentGeneration)
|
||||
{
|
||||
assert(scoredPopulation.size() == initialPopulation.size());
|
||||
std::cout << "Processing generation " << currentGeneration << "..." << std::endl;
|
||||
std::cout << "Need to create " << childrenCountPerGeneration << " new children" << std::endl;
|
||||
for (std::size_t currentGeneration {}; currentGeneration < _params.nbGenerations; ++currentGeneration)
|
||||
{
|
||||
assert(scoredPopulation.size() == initialPopulation.size());
|
||||
std::cout << "Processing generation " << currentGeneration << "..." << std::endl;
|
||||
std::cout << "Need to create " << childrenCountPerGeneration << " new children" << std::endl;
|
||||
|
||||
// breed
|
||||
const Score populationTotalScore {getTotalScore(scoredPopulation)};
|
||||
std::vector<ScoredIndividual> children;
|
||||
children.reserve(childrenCountPerGeneration);
|
||||
// breed
|
||||
const Score populationTotalScore {getTotalScore(scoredPopulation)};
|
||||
std::vector<ScoredIndividual> children;
|
||||
children.reserve(childrenCountPerGeneration);
|
||||
|
||||
while (children.size() < childrenCountPerGeneration)
|
||||
{
|
||||
// Select two random parents using their score as weight
|
||||
const auto itParent1 {pickRandomRouletteWheel(scoredPopulation, populationTotalScore)};
|
||||
const auto itParent2 {pickRandomRouletteWheel(scoredPopulation, populationTotalScore)};
|
||||
while (children.size() < childrenCountPerGeneration)
|
||||
{
|
||||
// Select two random parents using their score as weight
|
||||
const auto itParent1 {pickRandomRouletteWheel(scoredPopulation, populationTotalScore)};
|
||||
const auto itParent2 {pickRandomRouletteWheel(scoredPopulation, populationTotalScore)};
|
||||
|
||||
if (itParent1 == itParent2)
|
||||
continue;
|
||||
if (itParent1 == itParent2)
|
||||
continue;
|
||||
|
||||
ScoredIndividual child {_params.breedFunction(itParent1->individual, itParent2->individual)};
|
||||
|
||||
if (Random::getRealRandom(float {}, float {1}) <= _params.mutationProbability)
|
||||
_params.mutateFunction(child.individual);
|
||||
ScoredIndividual child {_params.breedFunction(itParent1->individual, itParent2->individual)};
|
||||
|
||||
if (core::random::getRealRandom(float {}, float {1}) <= _params.mutationProbability)
|
||||
_params.mutateFunction(child.individual);
|
||||
|
||||
children.emplace_back(std::move(child));
|
||||
}
|
||||
children.emplace_back(std::move(child));
|
||||
}
|
||||
|
||||
// Elitist selection
|
||||
scoredPopulation.resize(initialPopulation.size() - childrenCountPerGeneration);
|
||||
// Elitist selection
|
||||
scoredPopulation.resize(initialPopulation.size() - childrenCountPerGeneration);
|
||||
|
||||
scoredPopulation.insert(std::end(scoredPopulation), std::make_move_iterator(std::begin(children)), std::make_move_iterator(std::end(children)));
|
||||
assert(scoredPopulation.size() == initialPopulation.size());
|
||||
scoredPopulation.insert(std::end(scoredPopulation), std::make_move_iterator(std::begin(children)), std::make_move_iterator(std::end(children)));
|
||||
assert(scoredPopulation.size() == initialPopulation.size());
|
||||
|
||||
scoreAndSortPopulation(scoredPopulation);
|
||||
scoreAndSortPopulation(scoredPopulation);
|
||||
|
||||
std::cout << "Mean score = " << getTotalScore(scoredPopulation) / scoredPopulation.size() << std::endl;
|
||||
std::cout << "Current best score = " << *scoredPopulation.front().score << std::endl;
|
||||
}
|
||||
std::cout << "Mean score = " << getTotalScore(scoredPopulation) / scoredPopulation.size() << std::endl;
|
||||
std::cout << "Current best score = " << *scoredPopulation.front().score << std::endl;
|
||||
}
|
||||
|
||||
std::cout << "Best score = " << *scoredPopulation.front().score << std::endl;
|
||||
return scoredPopulation.front().individual;
|
||||
std::cout << "Best score = " << *scoredPopulation.front().score << std::endl;
|
||||
return scoredPopulation.front().individual;
|
||||
}
|
||||
|
||||
|
||||
@@ -135,39 +135,37 @@ template<typename Individual>
|
||||
void
|
||||
GeneticAlgorithm<Individual>::scoreAndSortPopulation(std::vector<ScoredIndividual>& scoredPopulation)
|
||||
{
|
||||
parallel_foreach(_params.nbWorkers, std::begin(scoredPopulation), std::end(scoredPopulation),
|
||||
[&](ScoredIndividual& scoredIndividual)
|
||||
{
|
||||
if (!scoredIndividual.score)
|
||||
scoredIndividual.score = _params.scoreFunction(scoredIndividual.individual);
|
||||
});
|
||||
parallel_foreach(_params.nbWorkers, std::begin(scoredPopulation), std::end(scoredPopulation),
|
||||
[&](ScoredIndividual& scoredIndividual)
|
||||
{
|
||||
if (!scoredIndividual.score)
|
||||
scoredIndividual.score = _params.scoreFunction(scoredIndividual.individual);
|
||||
});
|
||||
|
||||
std::sort(std::begin(scoredPopulation), std::end(scoredPopulation), [](const ScoredIndividual& a, const ScoredIndividual& b) { return a.score > b.score; });
|
||||
std::sort(std::begin(scoredPopulation), std::end(scoredPopulation), [](const ScoredIndividual& a, const ScoredIndividual& b) { return a.score > b.score; });
|
||||
}
|
||||
|
||||
template<typename Individual>
|
||||
typename GeneticAlgorithm<Individual>::Score
|
||||
GeneticAlgorithm<Individual>::getTotalScore(const std::vector<ScoredIndividual>& scoredPopulation) const
|
||||
{
|
||||
return std::accumulate(std::cbegin(scoredPopulation), std::cend(scoredPopulation), Score {}, [](Score score, const ScoredIndividual& individual) { return score + *individual.score; });
|
||||
return std::accumulate(std::cbegin(scoredPopulation), std::cend(scoredPopulation), Score {}, [](Score score, const ScoredIndividual& individual) { return score + *individual.score; });
|
||||
}
|
||||
|
||||
template<typename Individual>
|
||||
typename std::vector<typename GeneticAlgorithm<Individual>::ScoredIndividual>::const_iterator
|
||||
GeneticAlgorithm<Individual>::pickRandomRouletteWheel(const std::vector<ScoredIndividual>& population, Score totalScore)
|
||||
{
|
||||
const Score randomScore {Random::getRealRandom(Score {}, totalScore)};
|
||||
const Score randomScore {core::random::getRealRandom(Score {}, totalScore)};
|
||||
|
||||
Score curScore{};
|
||||
for (auto itScoredIndividual {std::cbegin(population)}; itScoredIndividual != std::cend(population); ++itScoredIndividual )
|
||||
{
|
||||
if (curScore + *itScoredIndividual->score > randomScore)
|
||||
return itScoredIndividual;
|
||||
Score curScore{};
|
||||
for (auto itScoredIndividual {std::cbegin(population)}; itScoredIndividual != std::cend(population); ++itScoredIndividual )
|
||||
{
|
||||
if (curScore + *itScoredIndividual->score > randomScore)
|
||||
return itScoredIndividual;
|
||||
|
||||
curScore += *itScoredIndividual->score;
|
||||
}
|
||||
curScore += *itScoredIndividual->score;
|
||||
}
|
||||
|
||||
throw std::runtime_error("bad random or empty population");
|
||||
throw std::runtime_error("bad random or empty population");
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -29,9 +29,9 @@
|
||||
#include "database/Track.hpp"
|
||||
#include "database/TrackFeatures.hpp"
|
||||
#include "similarity/features/SimilarityFeaturesSearcher.hpp"
|
||||
#include "utils/Config.hpp"
|
||||
#include "utils/Service.hpp"
|
||||
#include "utils/StreamLogger.hpp"
|
||||
#include "core/Config.hpp"
|
||||
#include "core/Service.hpp"
|
||||
#include "core/StreamLogger.hpp"
|
||||
|
||||
#include "GeneticAlgorithm.hpp"
|
||||
|
||||
@@ -42,446 +42,446 @@ using SimilarityScore = GeneticAlgorithm<FeatureSettingsMap>::Score;
|
||||
// The goal is to get the FeatureSettingsMap that maximize the score
|
||||
const FeatureSettingsMap featuresSettings
|
||||
{
|
||||
{ "lowlevel.average_loudness", {1}},
|
||||
{ "lowlevel.barkbands.mean", {1}},
|
||||
{ "lowlevel.barkbands.median", {1}},
|
||||
{ "lowlevel.barkbands.var", {1}},
|
||||
{ "lowlevel.barkbands_crest.mean", {1}},
|
||||
{ "lowlevel.barkbands_crest.median", {1}},
|
||||
{ "lowlevel.barkbands_crest.var", {1}},
|
||||
{ "lowlevel.barkbands_flatness_db.mean", {1}},
|
||||
{ "lowlevel.barkbands_flatness_db.median", {1}},
|
||||
{ "lowlevel.barkbands_flatness_db.var", {1}},
|
||||
{ "lowlevel.barkbands_kurtosis.mean", {1}},
|
||||
{ "lowlevel.barkbands_kurtosis.median", {1}},
|
||||
{ "lowlevel.barkbands_kurtosis.var", {1}},
|
||||
{ "lowlevel.barkbands_skewness.mean", {1}},
|
||||
{ "lowlevel.barkbands_skewness.median", {1}},
|
||||
{ "lowlevel.barkbands_skewness.var", {1}},
|
||||
{ "lowlevel.barkbands_spread.mean", {1}},
|
||||
{ "lowlevel.barkbands_spread.median", {1}},
|
||||
{ "lowlevel.barkbands_spread.var", {1}},
|
||||
{ "lowlevel.dissonance.mean", {1}},
|
||||
{ "lowlevel.dissonance.median", {1}},
|
||||
{ "lowlevel.dissonance.var", {1}},
|
||||
{ "lowlevel.dynamic_complexity", {1}},
|
||||
{ "lowlevel.spectral_contrast_coeffs.mean", {1}},
|
||||
{ "lowlevel.spectral_contrast_coeffs.median", {1}},
|
||||
{ "lowlevel.spectral_contrast_coeffs.var", {1}},
|
||||
{ "lowlevel.erbbands.mean", {1}},
|
||||
{ "lowlevel.erbbands.median", {1}},
|
||||
{ "lowlevel.erbbands.var", {1}},
|
||||
{ "lowlevel.gfcc.mean", {1}},
|
||||
{ "lowlevel.hfc.mean", {1}},
|
||||
{ "lowlevel.hfc.median", {1}},
|
||||
{ "lowlevel.hfc.var", {1}},
|
||||
{ "tonal.hpcp.median", {1}},
|
||||
{ "lowlevel.melbands.mean", {1}},
|
||||
{ "lowlevel.melbands.median", {1}},
|
||||
{ "lowlevel.melbands.var", {1}},
|
||||
{ "lowlevel.melbands_crest.mean", {1}},
|
||||
{ "lowlevel.melbands_crest.median", {1}},
|
||||
{ "lowlevel.melbands_crest.var", {1}},
|
||||
{ "lowlevel.melbands_flatness_db.mean", {1}},
|
||||
{ "lowlevel.melbands_flatness_db.median", {1}},
|
||||
{ "lowlevel.melbands_flatness_db.var", {1}},
|
||||
{ "lowlevel.melbands_kurtosis.mean", {1}},
|
||||
{ "lowlevel.melbands_kurtosis.median", {1}},
|
||||
{ "lowlevel.melbands_kurtosis.var", {1}},
|
||||
{ "lowlevel.melbands_skewness.mean", {1}},
|
||||
{ "lowlevel.melbands_skewness.median", {1}},
|
||||
{ "lowlevel.melbands_skewness.var", {1}},
|
||||
{ "lowlevel.melbands_spread.mean", {1}},
|
||||
{ "lowlevel.melbands_spread.median", {1}},
|
||||
{ "lowlevel.melbands_spread.var", {1}},
|
||||
{ "lowlevel.mfcc.mean", {1}},
|
||||
{ "lowlevel.pitch_salience.mean", {1}},
|
||||
{ "lowlevel.pitch_salience.median", {1}},
|
||||
{ "lowlevel.pitch_salience.var", {1}},
|
||||
{ "lowlevel.silence_rate_30dB.mean", {1}},
|
||||
{ "lowlevel.silence_rate_30dB.median", {1}},
|
||||
{ "lowlevel.silence_rate_30dB.var", {1}},
|
||||
{ "lowlevel.silence_rate_60dB.mean", {1}},
|
||||
{ "lowlevel.silence_rate_60dB.median", {1}},
|
||||
{ "lowlevel.silence_rate_60dB.var", {1}},
|
||||
{ "lowlevel.spectral_centroid.mean", {1}},
|
||||
{ "lowlevel.spectral_centroid.median", {1}},
|
||||
{ "lowlevel.spectral_centroid.var", {1}},
|
||||
{ "lowlevel.spectral_complexity.mean", {1}},
|
||||
{ "lowlevel.spectral_complexity.median", {1}},
|
||||
{ "lowlevel.spectral_complexity.var", {1}},
|
||||
{ "lowlevel.spectral_contrast_coeffs.mean", {1}},
|
||||
{ "lowlevel.spectral_contrast_coeffs.median", {1}},
|
||||
{ "lowlevel.spectral_contrast_coeffs.var", {1}},
|
||||
{ "lowlevel.spectral_contrast_valleys.mean", {1}},
|
||||
{ "lowlevel.spectral_contrast_valleys.median", {1}},
|
||||
{ "lowlevel.spectral_contrast_valleys.var", {1}},
|
||||
{ "lowlevel.spectral_decrease.mean", {1}},
|
||||
{ "lowlevel.spectral_decrease.median", {1}},
|
||||
{ "lowlevel.spectral_decrease.var", {1}},
|
||||
{ "lowlevel.spectral_energy.mean", {1}},
|
||||
{ "lowlevel.spectral_energy.median", {1}},
|
||||
{ "lowlevel.spectral_energy.var", {1}},
|
||||
{ "lowlevel.spectral_energyband_high.mean", {1}},
|
||||
{ "lowlevel.spectral_energyband_high.median", {1}},
|
||||
{ "lowlevel.spectral_energyband_high.var", {1}},
|
||||
{ "lowlevel.spectral_energyband_low.mean", {1}},
|
||||
{ "lowlevel.spectral_energyband_low.median", {1}},
|
||||
{ "lowlevel.spectral_energyband_low.var", {1}},
|
||||
{ "lowlevel.spectral_energyband_middle_high.mean", {1}},
|
||||
{ "lowlevel.spectral_energyband_middle_high.median", {1}},
|
||||
{ "lowlevel.spectral_energyband_middle_high.var", {1}},
|
||||
{ "lowlevel.spectral_energyband_middle_low.mean", {1}},
|
||||
{ "lowlevel.spectral_energyband_middle_low.median", {1}},
|
||||
{ "lowlevel.spectral_energyband_middle_low.var", {1}},
|
||||
{ "lowlevel.spectral_entropy.mean", {1}},
|
||||
{ "lowlevel.spectral_entropy.median", {1}},
|
||||
{ "lowlevel.spectral_entropy.var", {1}},
|
||||
{ "lowlevel.spectral_flux.mean", {1}},
|
||||
{ "lowlevel.spectral_flux.median", {1}},
|
||||
{ "lowlevel.spectral_flux.var", {1}},
|
||||
{ "lowlevel.spectral_kurtosis.mean", {1}},
|
||||
{ "lowlevel.spectral_kurtosis.median", {1}},
|
||||
{ "lowlevel.spectral_kurtosis.var", {1}},
|
||||
{ "lowlevel.spectral_rms.mean", {1}},
|
||||
{ "lowlevel.spectral_rms.median", {1}},
|
||||
{ "lowlevel.spectral_rms.var", {1}},
|
||||
{ "lowlevel.spectral_rolloff.mean", {1}},
|
||||
{ "lowlevel.spectral_rolloff.median", {1}},
|
||||
{ "lowlevel.spectral_rolloff.var", {1}},
|
||||
{ "lowlevel.spectral_skewness.mean", {1}},
|
||||
{ "lowlevel.spectral_skewness.median", {1}},
|
||||
{ "lowlevel.spectral_skewness.var", {1}},
|
||||
{ "lowlevel.spectral_spread.mean", {1}},
|
||||
{ "lowlevel.spectral_spread.median", {1}},
|
||||
{ "lowlevel.spectral_spread.var", {1}},
|
||||
{ "lowlevel.zerocrossingrate.mean", {1}},
|
||||
{ "lowlevel.zerocrossingrate.median", {1}},
|
||||
{ "lowlevel.zerocrossingrate.var", {1}},
|
||||
{ "lowlevel.average_loudness", {1}},
|
||||
{ "lowlevel.barkbands.mean", {1}},
|
||||
{ "lowlevel.barkbands.median", {1}},
|
||||
{ "lowlevel.barkbands.var", {1}},
|
||||
{ "lowlevel.barkbands_crest.mean", {1}},
|
||||
{ "lowlevel.barkbands_crest.median", {1}},
|
||||
{ "lowlevel.barkbands_crest.var", {1}},
|
||||
{ "lowlevel.barkbands_flatness_db.mean", {1}},
|
||||
{ "lowlevel.barkbands_flatness_db.median", {1}},
|
||||
{ "lowlevel.barkbands_flatness_db.var", {1}},
|
||||
{ "lowlevel.barkbands_kurtosis.mean", {1}},
|
||||
{ "lowlevel.barkbands_kurtosis.median", {1}},
|
||||
{ "lowlevel.barkbands_kurtosis.var", {1}},
|
||||
{ "lowlevel.barkbands_skewness.mean", {1}},
|
||||
{ "lowlevel.barkbands_skewness.median", {1}},
|
||||
{ "lowlevel.barkbands_skewness.var", {1}},
|
||||
{ "lowlevel.barkbands_spread.mean", {1}},
|
||||
{ "lowlevel.barkbands_spread.median", {1}},
|
||||
{ "lowlevel.barkbands_spread.var", {1}},
|
||||
{ "lowlevel.dissonance.mean", {1}},
|
||||
{ "lowlevel.dissonance.median", {1}},
|
||||
{ "lowlevel.dissonance.var", {1}},
|
||||
{ "lowlevel.dynamic_complexity", {1}},
|
||||
{ "lowlevel.spectral_contrast_coeffs.mean", {1}},
|
||||
{ "lowlevel.spectral_contrast_coeffs.median", {1}},
|
||||
{ "lowlevel.spectral_contrast_coeffs.var", {1}},
|
||||
{ "lowlevel.erbbands.mean", {1}},
|
||||
{ "lowlevel.erbbands.median", {1}},
|
||||
{ "lowlevel.erbbands.var", {1}},
|
||||
{ "lowlevel.gfcc.mean", {1}},
|
||||
{ "lowlevel.hfc.mean", {1}},
|
||||
{ "lowlevel.hfc.median", {1}},
|
||||
{ "lowlevel.hfc.var", {1}},
|
||||
{ "tonal.hpcp.median", {1}},
|
||||
{ "lowlevel.melbands.mean", {1}},
|
||||
{ "lowlevel.melbands.median", {1}},
|
||||
{ "lowlevel.melbands.var", {1}},
|
||||
{ "lowlevel.melbands_crest.mean", {1}},
|
||||
{ "lowlevel.melbands_crest.median", {1}},
|
||||
{ "lowlevel.melbands_crest.var", {1}},
|
||||
{ "lowlevel.melbands_flatness_db.mean", {1}},
|
||||
{ "lowlevel.melbands_flatness_db.median", {1}},
|
||||
{ "lowlevel.melbands_flatness_db.var", {1}},
|
||||
{ "lowlevel.melbands_kurtosis.mean", {1}},
|
||||
{ "lowlevel.melbands_kurtosis.median", {1}},
|
||||
{ "lowlevel.melbands_kurtosis.var", {1}},
|
||||
{ "lowlevel.melbands_skewness.mean", {1}},
|
||||
{ "lowlevel.melbands_skewness.median", {1}},
|
||||
{ "lowlevel.melbands_skewness.var", {1}},
|
||||
{ "lowlevel.melbands_spread.mean", {1}},
|
||||
{ "lowlevel.melbands_spread.median", {1}},
|
||||
{ "lowlevel.melbands_spread.var", {1}},
|
||||
{ "lowlevel.mfcc.mean", {1}},
|
||||
{ "lowlevel.pitch_salience.mean", {1}},
|
||||
{ "lowlevel.pitch_salience.median", {1}},
|
||||
{ "lowlevel.pitch_salience.var", {1}},
|
||||
{ "lowlevel.silence_rate_30dB.mean", {1}},
|
||||
{ "lowlevel.silence_rate_30dB.median", {1}},
|
||||
{ "lowlevel.silence_rate_30dB.var", {1}},
|
||||
{ "lowlevel.silence_rate_60dB.mean", {1}},
|
||||
{ "lowlevel.silence_rate_60dB.median", {1}},
|
||||
{ "lowlevel.silence_rate_60dB.var", {1}},
|
||||
{ "lowlevel.spectral_centroid.mean", {1}},
|
||||
{ "lowlevel.spectral_centroid.median", {1}},
|
||||
{ "lowlevel.spectral_centroid.var", {1}},
|
||||
{ "lowlevel.spectral_complexity.mean", {1}},
|
||||
{ "lowlevel.spectral_complexity.median", {1}},
|
||||
{ "lowlevel.spectral_complexity.var", {1}},
|
||||
{ "lowlevel.spectral_contrast_coeffs.mean", {1}},
|
||||
{ "lowlevel.spectral_contrast_coeffs.median", {1}},
|
||||
{ "lowlevel.spectral_contrast_coeffs.var", {1}},
|
||||
{ "lowlevel.spectral_contrast_valleys.mean", {1}},
|
||||
{ "lowlevel.spectral_contrast_valleys.median", {1}},
|
||||
{ "lowlevel.spectral_contrast_valleys.var", {1}},
|
||||
{ "lowlevel.spectral_decrease.mean", {1}},
|
||||
{ "lowlevel.spectral_decrease.median", {1}},
|
||||
{ "lowlevel.spectral_decrease.var", {1}},
|
||||
{ "lowlevel.spectral_energy.mean", {1}},
|
||||
{ "lowlevel.spectral_energy.median", {1}},
|
||||
{ "lowlevel.spectral_energy.var", {1}},
|
||||
{ "lowlevel.spectral_energyband_high.mean", {1}},
|
||||
{ "lowlevel.spectral_energyband_high.median", {1}},
|
||||
{ "lowlevel.spectral_energyband_high.var", {1}},
|
||||
{ "lowlevel.spectral_energyband_low.mean", {1}},
|
||||
{ "lowlevel.spectral_energyband_low.median", {1}},
|
||||
{ "lowlevel.spectral_energyband_low.var", {1}},
|
||||
{ "lowlevel.spectral_energyband_middle_high.mean", {1}},
|
||||
{ "lowlevel.spectral_energyband_middle_high.median", {1}},
|
||||
{ "lowlevel.spectral_energyband_middle_high.var", {1}},
|
||||
{ "lowlevel.spectral_energyband_middle_low.mean", {1}},
|
||||
{ "lowlevel.spectral_energyband_middle_low.median", {1}},
|
||||
{ "lowlevel.spectral_energyband_middle_low.var", {1}},
|
||||
{ "lowlevel.spectral_entropy.mean", {1}},
|
||||
{ "lowlevel.spectral_entropy.median", {1}},
|
||||
{ "lowlevel.spectral_entropy.var", {1}},
|
||||
{ "lowlevel.spectral_flux.mean", {1}},
|
||||
{ "lowlevel.spectral_flux.median", {1}},
|
||||
{ "lowlevel.spectral_flux.var", {1}},
|
||||
{ "lowlevel.spectral_kurtosis.mean", {1}},
|
||||
{ "lowlevel.spectral_kurtosis.median", {1}},
|
||||
{ "lowlevel.spectral_kurtosis.var", {1}},
|
||||
{ "lowlevel.spectral_rms.mean", {1}},
|
||||
{ "lowlevel.spectral_rms.median", {1}},
|
||||
{ "lowlevel.spectral_rms.var", {1}},
|
||||
{ "lowlevel.spectral_rolloff.mean", {1}},
|
||||
{ "lowlevel.spectral_rolloff.median", {1}},
|
||||
{ "lowlevel.spectral_rolloff.var", {1}},
|
||||
{ "lowlevel.spectral_skewness.mean", {1}},
|
||||
{ "lowlevel.spectral_skewness.median", {1}},
|
||||
{ "lowlevel.spectral_skewness.var", {1}},
|
||||
{ "lowlevel.spectral_spread.mean", {1}},
|
||||
{ "lowlevel.spectral_spread.median", {1}},
|
||||
{ "lowlevel.spectral_spread.var", {1}},
|
||||
{ "lowlevel.zerocrossingrate.mean", {1}},
|
||||
{ "lowlevel.zerocrossingrate.median", {1}},
|
||||
{ "lowlevel.zerocrossingrate.var", {1}},
|
||||
};
|
||||
|
||||
static
|
||||
std::unordered_map<Database::IdType, FeatureValuesMap>
|
||||
constructFeaturesCache(Database::Session& session, const FeatureSettingsMap& featureSettings)
|
||||
std::unordered_map<db::IdType, FeatureValuesMap>
|
||||
constructFeaturesCache(db::Session& session, const FeatureSettingsMap& featureSettings)
|
||||
{
|
||||
std::unordered_map<Database::IdType, FeatureValuesMap> cache;
|
||||
std::unordered_map<db::IdType, FeatureValuesMap> cache;
|
||||
|
||||
std::unordered_set<FeatureName> names;
|
||||
std::transform(std::cbegin(featureSettings), std::cend(featureSettings), std::inserter(names, std::begin(names)),
|
||||
[](const auto& itFeature) { return itFeature.first; });
|
||||
std::unordered_set<FeatureName> names;
|
||||
std::transform(std::cbegin(featureSettings), std::cend(featureSettings), std::inserter(names, std::begin(names)),
|
||||
[](const auto& itFeature) { return itFeature.first; });
|
||||
|
||||
auto transaction {session.createReadTransaction()};
|
||||
auto transaction{ session.createReadTransaction() };
|
||||
|
||||
for (auto trackId : Database::Track::getAllIdsWithFeatures(session))
|
||||
{
|
||||
const Database::Track::pointer track {Database::Track::getById(session, trackId)};
|
||||
const Database::TrackFeatures::pointer trackFeatures {track->getTrackFeatures()};
|
||||
for (auto trackId : db::Track::getAllIdsWithFeatures(session))
|
||||
{
|
||||
const db::Track::pointer track{ db::Track::getById(session, trackId) };
|
||||
const db::TrackFeatures::pointer trackFeatures{ track->getTrackFeatures() };
|
||||
|
||||
cache[trackId] = trackFeatures->getFeatureValuesMap(names);
|
||||
}
|
||||
cache[trackId] = trackFeatures->getFeatureValuesMap(names);
|
||||
}
|
||||
|
||||
return cache;
|
||||
return cache;
|
||||
}
|
||||
|
||||
static
|
||||
std::optional<FeatureValuesMap>
|
||||
getFeaturesFromCache(const std::unordered_map<Database::IdType, FeatureValuesMap>& cache, Database::IdType trackId, const FeatureNames& names)
|
||||
getFeaturesFromCache(const std::unordered_map<db::IdType, FeatureValuesMap>& cache, db::IdType trackId, const FeatureNames& names)
|
||||
{
|
||||
std::optional<FeatureValuesMap> res;
|
||||
std::optional<FeatureValuesMap> res;
|
||||
|
||||
auto it {cache.find(trackId)};
|
||||
if (it == std::cend(cache))
|
||||
return res;
|
||||
auto it{ cache.find(trackId) };
|
||||
if (it == std::cend(cache))
|
||||
return res;
|
||||
|
||||
res = FeatureValuesMap{};
|
||||
res = FeatureValuesMap{};
|
||||
|
||||
const FeatureValuesMap& trackFeatures {it->second};
|
||||
for (const FeatureName& name : names)
|
||||
{
|
||||
auto itFeatures {trackFeatures.find(name)};
|
||||
if (itFeatures == std::cend(trackFeatures))
|
||||
{
|
||||
res.reset();
|
||||
break;
|
||||
}
|
||||
const FeatureValuesMap& trackFeatures{ it->second };
|
||||
for (const FeatureName& name : names)
|
||||
{
|
||||
auto itFeatures{ trackFeatures.find(name) };
|
||||
if (itFeatures == std::cend(trackFeatures))
|
||||
{
|
||||
res.reset();
|
||||
break;
|
||||
}
|
||||
|
||||
res->emplace(name, itFeatures ->second);
|
||||
}
|
||||
res->emplace(name, itFeatures->second);
|
||||
}
|
||||
|
||||
return res;
|
||||
return res;
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
printFeatureSettingsMap(const FeatureSettingsMap& featureSettings)
|
||||
{
|
||||
std::cout << "FeatureSettingsMap: (" << featureSettings.size() << " features)" << std::endl;
|
||||
for (const auto& [name, settings] : featureSettings)
|
||||
std::cout << "\t" << name << std::endl;
|
||||
std::cout << "FeatureSettingsMap: (" << featureSettings.size() << " features)" << std::endl;
|
||||
for (const auto& [name, settings] : featureSettings)
|
||||
std::cout << "\t" << name << std::endl;
|
||||
}
|
||||
|
||||
static
|
||||
std::string
|
||||
trackToString(Database::Session& session, Database::IdType trackId)
|
||||
trackToString(db::Session& session, db::IdType trackId)
|
||||
{
|
||||
std::string res;
|
||||
auto transaction {session.createReadTransaction()};
|
||||
Database::Track::pointer track {Database::Track::getById(session, trackId)};
|
||||
std::string res;
|
||||
auto transaction{ session.createReadTransaction() };
|
||||
db::Track::pointer track{ db::Track::getById(session, trackId) };
|
||||
|
||||
res += track->getName();
|
||||
if (track->getRelease())
|
||||
res += " [" + track->getRelease()->getName() + "]";
|
||||
for (auto artist : track->getArtists())
|
||||
res += " - " + artist->getName();
|
||||
for (auto cluster : track->getClusters())
|
||||
res += " {" + cluster->getType()->getName() + "-"+ cluster->getName() + "}";
|
||||
res += track->getName();
|
||||
if (track->getRelease())
|
||||
res += " [" + track->getRelease()->getName() + "]";
|
||||
for (auto artist : track->getArtists())
|
||||
res += " - " + artist->getName();
|
||||
for (auto cluster : track->getClusters())
|
||||
res += " {" + cluster->getType()->getName() + "-" + cluster->getName() + "}";
|
||||
|
||||
return res;
|
||||
return res;
|
||||
}
|
||||
|
||||
static
|
||||
SimilarityScore
|
||||
computeTrackScore(Database::Session& session, Database::IdType track1Id, Database::IdType track2Id)
|
||||
computeTrackScore(db::Session& session, db::IdType track1Id, db::IdType track2Id)
|
||||
{
|
||||
SimilarityScore score {};
|
||||
SimilarityScore score{};
|
||||
|
||||
auto transaction {session.createReadTransaction()};
|
||||
auto transaction{ session.createReadTransaction() };
|
||||
|
||||
auto track1 {Database::Track::getById(session, track1Id)};
|
||||
auto track2 {Database::Track::getById(session, track2Id)};
|
||||
auto track1{ db::Track::getById(session, track1Id) };
|
||||
auto track2{ db::Track::getById(session, track2Id) };
|
||||
|
||||
if (track1->getRelease() == track2->getRelease())
|
||||
score += 1;
|
||||
if (track1->getRelease() == track2->getRelease())
|
||||
score += 1;
|
||||
|
||||
// Artists in common
|
||||
{
|
||||
auto track1ArtistIds {track1->getArtistIds()};
|
||||
auto track2ArtistIds {track2->getArtistIds()};
|
||||
// Artists in common
|
||||
{
|
||||
auto track1ArtistIds{ track1->getArtistIds() };
|
||||
auto track2ArtistIds{ track2->getArtistIds() };
|
||||
|
||||
std::vector<Database::IdType> commonArtistIds;
|
||||
std::set_intersection(std::cbegin(track1ArtistIds), std::cend(track1ArtistIds),
|
||||
std::cbegin(track2ArtistIds), std::cend(track2ArtistIds),
|
||||
std::back_inserter(commonArtistIds));
|
||||
std::vector<db::IdType> commonArtistIds;
|
||||
std::set_intersection(std::cbegin(track1ArtistIds), std::cend(track1ArtistIds),
|
||||
std::cbegin(track2ArtistIds), std::cend(track2ArtistIds),
|
||||
std::back_inserter(commonArtistIds));
|
||||
|
||||
score += commonArtistIds.size();
|
||||
}
|
||||
score += commonArtistIds.size();
|
||||
}
|
||||
|
||||
// Clusters in common
|
||||
{
|
||||
auto track1ClusterIds {track1->getClusterIds()};
|
||||
auto track2ClusterIds {track2->getClusterIds()};
|
||||
// Clusters in common
|
||||
{
|
||||
auto track1ClusterIds{ track1->getClusterIds() };
|
||||
auto track2ClusterIds{ track2->getClusterIds() };
|
||||
|
||||
std::vector<Database::IdType> commonClusterIds;
|
||||
std::set_intersection(std::cbegin(track1ClusterIds), std::cend(track1ClusterIds),
|
||||
std::cbegin(track2ClusterIds), std::cend(track2ClusterIds),
|
||||
std::back_inserter(commonClusterIds));
|
||||
std::vector<db::IdType> commonClusterIds;
|
||||
std::set_intersection(std::cbegin(track1ClusterIds), std::cend(track1ClusterIds),
|
||||
std::cbegin(track2ClusterIds), std::cend(track2ClusterIds),
|
||||
std::back_inserter(commonClusterIds));
|
||||
|
||||
score += commonClusterIds.size();
|
||||
}
|
||||
score += commonClusterIds.size();
|
||||
}
|
||||
|
||||
return score;
|
||||
return score;
|
||||
}
|
||||
|
||||
static
|
||||
SimilarityScore
|
||||
computeSimilarityScore(Database::Session& session, FeaturesSearcher::TrainSettings trainSettings)
|
||||
computeSimilarityScore(db::Session& session, FeaturesSearcher::TrainSettings trainSettings)
|
||||
{
|
||||
std::cout << "Compute score of: ";
|
||||
printFeatureSettingsMap(trainSettings.featureSettingsMap);
|
||||
std::cout << std::endl;
|
||||
std::cout << "Compute score of: ";
|
||||
printFeatureSettingsMap(trainSettings.featureSettingsMap);
|
||||
std::cout << std::endl;
|
||||
|
||||
FeaturesSearcher searcher {session, trainSettings};
|
||||
FeaturesSearcher searcher{ session, trainSettings };
|
||||
|
||||
const std::vector<Database::IdType> trackIds = std::invoke([&]()
|
||||
{
|
||||
auto transaction {session.createReadTransaction()};
|
||||
return Database::Track::getAllIdsWithFeatures(session);
|
||||
});
|
||||
const std::vector<db::IdType> trackIds = std::invoke([&]()
|
||||
{
|
||||
auto transaction{ session.createReadTransaction() };
|
||||
return db::Track::getAllIdsWithFeatures(session);
|
||||
});
|
||||
|
||||
SimilarityScore score {};
|
||||
for (Database::IdType trackId : trackIds)
|
||||
{
|
||||
constexpr std::size_t nbSimilarTracks {3};
|
||||
// std::cout << "Processing track '" << trackToString(session, trackId) << "'" << std::endl;
|
||||
SimilarityScore factor {1};
|
||||
for (Database::IdType similarTrackId : searcher.getSimilarTracks({trackId}, nbSimilarTracks))
|
||||
{
|
||||
SimilarityScore trackScore {computeTrackScore(session, trackId, similarTrackId)};
|
||||
// std::cout << "\tScore = " << trackScore << " (*" << factor << ") with track '" << trackToString(session, similarTrackId) << "'" << std::endl;
|
||||
trackScore *= factor;
|
||||
score += trackScore;
|
||||
SimilarityScore score{};
|
||||
for (db::IdType trackId : trackIds)
|
||||
{
|
||||
constexpr std::size_t nbSimilarTracks{ 3 };
|
||||
// std::cout << "Processing track '" << trackToString(session, trackId) << "'" << std::endl;
|
||||
SimilarityScore factor{ 1 };
|
||||
for (db::IdType similarTrackId : searcher.getSimilarTracks({ trackId }, nbSimilarTracks))
|
||||
{
|
||||
SimilarityScore trackScore{ computeTrackScore(session, trackId, similarTrackId) };
|
||||
// std::cout << "\tScore = " << trackScore << " (*" << factor << ") with track '" << trackToString(session, similarTrackId) << "'" << std::endl;
|
||||
trackScore *= factor;
|
||||
score += trackScore;
|
||||
|
||||
factor -= (SimilarityScore {1}/nbSimilarTracks );
|
||||
}
|
||||
}
|
||||
factor -= (SimilarityScore{ 1 } / nbSimilarTracks);
|
||||
}
|
||||
}
|
||||
|
||||
std::cout << "Total score = " << score << std::endl;
|
||||
std::cout << "Total score = " << score << std::endl;
|
||||
|
||||
return score;
|
||||
return score;
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
printBadlyClassifiedTracks(Database::Session& session, FeaturesSearcher::TrainSettings trainSettings)
|
||||
printBadlyClassifiedTracks(db::Session& session, FeaturesSearcher::TrainSettings trainSettings)
|
||||
{
|
||||
FeaturesSearcher searcher {session, trainSettings};
|
||||
FeaturesSearcher searcher{ session, trainSettings };
|
||||
|
||||
const std::vector<Database::IdType> trackIds = std::invoke([&]()
|
||||
{
|
||||
auto transaction {session.createReadTransaction()};
|
||||
return Database::Track::getAllIdsWithFeatures(session);
|
||||
});
|
||||
const std::vector<db::IdType> trackIds = std::invoke([&]()
|
||||
{
|
||||
auto transaction{ session.createReadTransaction() };
|
||||
return db::Track::getAllIdsWithFeatures(session);
|
||||
});
|
||||
|
||||
for (Database::IdType trackId : trackIds)
|
||||
{
|
||||
constexpr std::size_t nbSimilarTracks {3};
|
||||
for (Database::IdType similarTrackId : searcher.getSimilarTracks({trackId}, nbSimilarTracks))
|
||||
{
|
||||
SimilarityScore trackScore {computeTrackScore(session, trackId, similarTrackId)};
|
||||
if (trackScore == 0)
|
||||
std::cout << "Badly classified tracks: '" << trackToString(session, trackId) << "'\n\twith track '" << trackToString(session, similarTrackId) << "'" <<std::endl;
|
||||
}
|
||||
}
|
||||
for (db::IdType trackId : trackIds)
|
||||
{
|
||||
constexpr std::size_t nbSimilarTracks{ 3 };
|
||||
for (db::IdType similarTrackId : searcher.getSimilarTracks({ trackId }, nbSimilarTracks))
|
||||
{
|
||||
SimilarityScore trackScore{ computeTrackScore(session, trackId, similarTrackId) };
|
||||
if (trackScore == 0)
|
||||
std::cout << "Badly classified tracks: '" << trackToString(session, trackId) << "'\n\twith track '" << trackToString(session, similarTrackId) << "'" << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static
|
||||
FeatureSettingsMap
|
||||
FeatureSettingsMap
|
||||
breedFeatureSettingsMap(const FeatureSettingsMap& a, const FeatureSettingsMap& b)
|
||||
{
|
||||
FeatureSettingsMap res;
|
||||
FeatureSettingsMap res;
|
||||
|
||||
res.insert(std::cbegin(a), std::cend(a));
|
||||
res.insert(std::cbegin(b), std::cend(b));
|
||||
res.insert(std::cbegin(a), std::cend(a));
|
||||
res.insert(std::cbegin(b), std::cend(b));
|
||||
|
||||
// just kill random elements until size is good
|
||||
while (res.size() > a.size())
|
||||
{
|
||||
const auto itFeature {Random::pickRandom(res)};
|
||||
res.erase(itFeature);
|
||||
}
|
||||
// just kill random elements until size is good
|
||||
while (res.size() > a.size())
|
||||
{
|
||||
const auto itFeature{ core::random::pickRandom(res) };
|
||||
res.erase(itFeature);
|
||||
}
|
||||
|
||||
return res;
|
||||
return res;
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
mutateFeatureSettingsMap(FeatureSettingsMap& a)
|
||||
{
|
||||
const std::size_t size {a.size()};
|
||||
// Replace one of the feature with another one, random
|
||||
a.erase(Random::pickRandom(a));
|
||||
const std::size_t size{ a.size() };
|
||||
// Replace one of the feature with another one, random
|
||||
a.erase(core::random::pickRandom(a));
|
||||
|
||||
while (a.size() != size)
|
||||
{
|
||||
const auto itFeatureSetting {Random::pickRandom(featuresSettings)};
|
||||
a.emplace(itFeatureSetting->first, itFeatureSetting->second);
|
||||
}
|
||||
while (a.size() != size)
|
||||
{
|
||||
const auto itFeatureSetting{ core::random::pickRandom(featuresSettings) };
|
||||
a.emplace(itFeatureSetting->first, itFeatureSetting->second);
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
try
|
||||
{
|
||||
// log to stdout
|
||||
try
|
||||
{
|
||||
// log to stdout
|
||||
// ServiceProvider<Logger>::create<StreamLogger>(std::cout);
|
||||
|
||||
if (argc != 3)
|
||||
{
|
||||
std::cerr << "usage: <lms_conf_file> <nb_workers>" << std::endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
if (argc != 3)
|
||||
{
|
||||
std::cerr << "usage: <lms_conf_file> <nb_workers>" << std::endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
const std::filesystem::path configFilePath {std::string(argv[1], 0, 256)};
|
||||
const std::size_t nbWorkers = atoi(argv[2]);
|
||||
const std::filesystem::path configFilePath{ std::string(argv[1], 0, 256) };
|
||||
const std::size_t nbWorkers = atoi(argv[2]);
|
||||
|
||||
ServiceProvider<Config>::create(configFilePath);
|
||||
ServiceProvider<Config>::create(configFilePath);
|
||||
|
||||
Database::Db db {ServiceProvider<Config>::get()->getPath("working-dir") / "lms.db"};
|
||||
Database::SessionPool sessionPool {db, nbWorkers};
|
||||
db::Db db{ ServiceProvider<Config>::get()->getPath("working-dir") / "lms.db" };
|
||||
db::SessionPool sessionPool{ db, nbWorkers };
|
||||
|
||||
std::cout << "Caching all features..." << std::endl;
|
||||
// Cache all the features of all the music in order to speed up the multiple trainings
|
||||
const auto cachedFeatures { constructFeaturesCache(Database::SessionPool::ScopedSession {sessionPool}.get(), featuresSettings) };
|
||||
std::cout << "Caching all features DONE" << std::endl;
|
||||
std::cout << "Caching all features..." << std::endl;
|
||||
// Cache all the features of all the music in order to speed up the multiple trainings
|
||||
const auto cachedFeatures{ constructFeaturesCache(db::SessionPool::ScopedSession {sessionPool}.get(), featuresSettings) };
|
||||
std::cout << "Caching all features DONE" << std::endl;
|
||||
|
||||
FeaturesSearcher::setFeaturesFetchFunc(
|
||||
[&](Database::IdType trackId, const FeatureNames& featureNames)
|
||||
{
|
||||
return getFeaturesFromCache(cachedFeatures, trackId, featureNames);
|
||||
});
|
||||
FeaturesSearcher::setFeaturesFetchFunc(
|
||||
[&](db::IdType trackId, const FeatureNames& featureNames)
|
||||
{
|
||||
return getFeaturesFromCache(cachedFeatures, trackId, featureNames);
|
||||
});
|
||||
|
||||
// Create some random settings (i.e random population)
|
||||
std::vector<FeatureSettingsMap> initialPopulation;
|
||||
// Create some random settings (i.e random population)
|
||||
std::vector<FeatureSettingsMap> initialPopulation;
|
||||
|
||||
constexpr std::size_t populationSize {200};
|
||||
constexpr std::size_t nbFeatures {5};
|
||||
constexpr std::size_t populationSize{ 200 };
|
||||
constexpr std::size_t nbFeatures{ 5 };
|
||||
|
||||
for (std::size_t i {}; i < populationSize; ++i)
|
||||
{
|
||||
FeatureSettingsMap settings;
|
||||
for (std::size_t i{}; i < populationSize; ++i)
|
||||
{
|
||||
FeatureSettingsMap settings;
|
||||
|
||||
while (settings.size() < nbFeatures)
|
||||
{
|
||||
const auto itFeatureSetting {Random::pickRandom(featuresSettings)};
|
||||
settings.emplace(itFeatureSetting->first, itFeatureSetting->second);
|
||||
}
|
||||
while (settings.size() < nbFeatures)
|
||||
{
|
||||
const auto itFeatureSetting{ core::random::pickRandom(featuresSettings) };
|
||||
settings.emplace(itFeatureSetting->first, itFeatureSetting->second);
|
||||
}
|
||||
|
||||
initialPopulation.emplace_back(std::move(settings));
|
||||
}
|
||||
initialPopulation.emplace_back(std::move(settings));
|
||||
}
|
||||
|
||||
FeaturesSearcher::TrainSettings trainSettings;
|
||||
trainSettings.iterationCount = 8;
|
||||
trainSettings.sampleCountPerNeuron = 1.5;
|
||||
FeaturesSearcher::TrainSettings trainSettings;
|
||||
trainSettings.iterationCount = 8;
|
||||
trainSettings.sampleCountPerNeuron = 1.5;
|
||||
|
||||
GeneticAlgorithm<FeatureSettingsMap>::Params params;
|
||||
params.nbWorkers = nbWorkers;
|
||||
params.nbGenerations = 1;
|
||||
params.crossoverRatio = 0.78;
|
||||
params.mutationProbability = 0.2;
|
||||
params.breedFunction = breedFeatureSettingsMap;
|
||||
params.mutateFunction = mutateFeatureSettingsMap;
|
||||
params.scoreFunction =
|
||||
[&](const FeatureSettingsMap& featureSettings)
|
||||
{
|
||||
FeaturesSearcher::TrainSettings settings {trainSettings};
|
||||
settings.featureSettingsMap = featureSettings;
|
||||
GeneticAlgorithm<FeatureSettingsMap>::Params params;
|
||||
params.nbWorkers = nbWorkers;
|
||||
params.nbGenerations = 1;
|
||||
params.crossoverRatio = 0.78;
|
||||
params.mutationProbability = 0.2;
|
||||
params.breedFunction = breedFeatureSettingsMap;
|
||||
params.mutateFunction = mutateFeatureSettingsMap;
|
||||
params.scoreFunction =
|
||||
[&](const FeatureSettingsMap& featureSettings)
|
||||
{
|
||||
FeaturesSearcher::TrainSettings settings{ trainSettings };
|
||||
settings.featureSettingsMap = featureSettings;
|
||||
|
||||
Database::SessionPool::ScopedSession scopedSession {sessionPool};
|
||||
return computeSimilarityScore(scopedSession.get(), settings);
|
||||
};
|
||||
db::SessionPool::ScopedSession scopedSession{ sessionPool };
|
||||
return computeSimilarityScore(scopedSession.get(), settings);
|
||||
};
|
||||
|
||||
GeneticAlgorithm<FeatureSettingsMap> geneticAlgorithm {params};
|
||||
GeneticAlgorithm<FeatureSettingsMap> geneticAlgorithm{ params };
|
||||
|
||||
std::cout << "Parameters:\n"
|
||||
<< "\tnb total settings = "<< featuresSettings.size() << "\n"
|
||||
<< "\tnb generations = " << params.nbGenerations << "\n"
|
||||
<< "\tpopulationSize = " << populationSize << "\n"
|
||||
<< "\tnbFeatures = " << nbFeatures << "\n"
|
||||
<< "\tcrossoverRatio = " << params.crossoverRatio << "\n"
|
||||
<< "\tmutationProbability = " << params.mutationProbability << "\n"
|
||||
<< std::endl;
|
||||
|
||||
std::cout << "Starting simulation..." << std::endl;
|
||||
const FeatureSettingsMap selectedSettings {geneticAlgorithm.simulate(initialPopulation)};
|
||||
std::cout << "Simulation complete! Best result:" << std::endl;
|
||||
printFeatureSettingsMap(selectedSettings);
|
||||
std::cout << "Parameters:\n"
|
||||
<< "\tnb total settings = " << featuresSettings.size() << "\n"
|
||||
<< "\tnb generations = " << params.nbGenerations << "\n"
|
||||
<< "\tpopulationSize = " << populationSize << "\n"
|
||||
<< "\tnbFeatures = " << nbFeatures << "\n"
|
||||
<< "\tcrossoverRatio = " << params.crossoverRatio << "\n"
|
||||
<< "\tmutationProbability = " << params.mutationProbability << "\n"
|
||||
<< std::endl;
|
||||
|
||||
// print all badly classified tracks
|
||||
{
|
||||
FeaturesSearcher::TrainSettings settings {trainSettings};
|
||||
settings.featureSettingsMap = selectedSettings;
|
||||
std::cout << "Starting simulation..." << std::endl;
|
||||
const FeatureSettingsMap selectedSettings{ geneticAlgorithm.simulate(initialPopulation) };
|
||||
std::cout << "Simulation complete! Best result:" << std::endl;
|
||||
printFeatureSettingsMap(selectedSettings);
|
||||
|
||||
Database::SessionPool::ScopedSession scopedSession {sessionPool};
|
||||
printBadlyClassifiedTracks(scopedSession.get(), settings);
|
||||
}
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
std::cerr << "Caught exception: " << e.what() << std::endl;
|
||||
}
|
||||
// print all badly classified tracks
|
||||
{
|
||||
FeaturesSearcher::TrainSettings settings{ trainSettings };
|
||||
settings.featureSettingsMap = selectedSettings;
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
db::SessionPool::ScopedSession scopedSession{ sessionPool };
|
||||
printBadlyClassifiedTracks(scopedSession.get(), settings);
|
||||
}
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
std::cerr << "Caught exception: " << e.what() << std::endl;
|
||||
}
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user