自然语言处理之Python实现中文分词

中文分词实现 1 问题描述 中文分词指的是将一个汉字序列切分成一个一个单独的词,中文分词是文本挖掘的基础,对于输入的一段中文,成功的进行中文分词

本文包含相关资料包-----> 点击直达获取<-------

中文分词实现

1 问题描述

中文分词指的是将一个汉字序列切分成一个一个单独的词。中文分词是文本挖掘的基础,对于输入的一段中文,成功的进行中文分词,可以达到电脑自动识别语句含义的效果。它是信息提取、信息检索、机器翻译、文本分类、自动文摘、语音识别、文本语音转换、自然语言理解等中文信息处理领域的基础。

1.1 基础任务

  1. 实现基于词典的分词算法

实验一资料包下的“Dictionary_based”文件夹中提供了基础词典和分词算法的大致框架。分词算法的核心部分需要大家完成,实验中提供了若干测试样本用以帮助大家判断算法是否正确实现。

  1. 实现基于统计学习的分词算法

实验中给出Bi-LSTM+CRF模型的基础实现,相关代码及说明文档位于实验一资料包下的“Bi-LSTM+CRF”文件夹下。请根据给定的实验资料中README.md文件配置相应实验环境,说明:(1)提供源码PyTorch语言编写(可根据个人掌情况用其他语言编写),默认运行版本是CPU版本;(2)如希望运行NPU版本,大家可跟任课老师联系,申请华为云资源运行(需提前统计名单:姓名+学号+个人手机号码+邮箱);

1.2 选做任务

优化基础任务中实现的分词器,可考虑的优化方案有:

  1. 修改网络结构,例如引入BERT等预训练语言模型;
  2. 与命名实体识别算法相互配合,减少对命名实体的错误分割;
  3. 构造合适的词典集(可扩充+人工整理);
  4. 实现新词发现(登录)功能,识别测试集中的新词(未登录词);
  5. 调整、优化模型训练过程中的超参数。

完成优化后对测试文件“Bi-LSTM+CRF/data/test.txt”进行分词,分词结果保存到.txt文件中utf-8编码,词与词之间以空格分隔,每个测试样本占一行。文件“Bi-LSTM+CRF/cws_result.txt”中给出了输出示例。提交分词结果后,依据单词级别的F1-score进行评判,决定选做部分的实验分数。

单词级别的F1-score的计算方式如下:

Gold: 共同 创造 美好 的 新 世纪 —— 二○○一年 新年 贺词

Hypothesis: 共同 创造 美 好 的 新 世纪 —— 二○○一年 新年 贺词

Precision = 9 / 11 = 0.818

Recall = 9 / 10 = 0.9

F1-score = 2*Precision*Recall/(Precision+Recall)=0.857

2 模块设计-基于词典

2.1 前向最大匹配

从待分词句子的左边向右边搜索,寻找词的最大匹配。我们需要规定一个词的最大长度,每次扫描的时候寻找当前开始的这个长度的词来和字典中的词匹配,如果没有找到,就缩短长度继续寻找,直到找到字典中的词或者成为单字。

算法流程如下:

(1)从待分词子串中从前往后取出max_len个字,然后扫描分词字典,测试该max_len个字的子串是否在字典中;

(2)如果存在,则从待分词子串中删除掉该max_len个字的子串,重新按照规则取子串,重复(1);

(3)如果不存在于字典中,则减少该子串的最右一个字,之后重复(1)。

2.2 后向最大匹配

从待分词句子的右边向左边搜索,寻找词的最大匹配。同样,我们也需要规定一个词的最大长度,每次扫描的时候寻找当前开始的这个长度的词来和字典中的词匹配,如果没有找到,就缩短长度继续寻找,直到找到字典中的词或者成为单字。

算法流程如下:

(1)从待分词子串中从后往前取出max_len个字,然后扫描分词字典,测试该max_len个字的子串是否在字典中;

(2)如果存在,则从待分词子串中删除掉该max_len个字的子串,重新按照规则取子串,重复(1);

(3)如果不存在于字典中,则减少该子串的最左一个字,之后重复(1)。

2.3 双向最大匹配

将前向最大匹配算法和后向最大匹配算法进行比较,从而确定正确的分词方法。

算法流程如下:

(1)比较前向最大匹配和后向最大匹配结果;

(2)如果分词结果相同,返回其中任意一个;

(3)如果分词结果不同:

1、比较两者分词总数量,数量高者罚分;

2、比较两者分词中单字词数量,单字词多者罚分;

3、比较两者分词中非字典词数量,非字典词多者罚分;

4、选择罚分最少的作为最终结果。

3 模块设计-基于统计学习

3.1 data_u.py

(1) getist:单个分词转换成tag序列。按行读入数据,并分析各个字对应的标签,然后返回分析结果。

(2) handle_data:处理数据,并保存至save_path。按行读取对应文件中的数据,并做相应的处理,然后把处理的结果保存到data_save.pkl中。

3.2 dataloader.py

读取通过data_u.py处理完后的文件data_save.pkl,并将其向量化。

3.3 infer.py

通过已经训练好的模型,完成对测试文件的分析,并将分词结果保存到cws_result.txt文件中。

3.4 model.py

(1) init_hidden:通过torch.randn函数进行初始化操作。

(2) _get_lstm_features:获取LSTM框架。

(3) forward:预测每个标签的loss值,以减少无效预测。

(4) infer:采用Bi-LSTM+CRF的基础结构的分析结果。

3.5 run.py

采用小批量梯度下降法,对模型进行训练,使得loss值降低。

小批量梯度下降,是对批量梯度下降以及随机梯度下降的一个折中办法。其思想是:每次迭代 使用 batch_size个样本来对参数进行更新,每次使用一个batch可以大大减小收敛所需要的迭代次数,同时可以使收敛到的结果更加接近梯度下降的效果。

3 代码实现-基于词典

3.1 前向最大匹配

```python def forward_mm_split(self, fmm_text): """ 正向最大匹配分词算法 :param fmm_text: 待分词字符串 :return: 分词结果,以list形式存放,每个元素为分出的词 """ # 字词列表,存放分词结果 word_list = [] # 用于记录分词的起始位置 count = 0 # 字或词当前的长度 word_len = self.max_len while word_len > 0 and count < len(fmm_text): word = fmm_text[count:count + word_len] word_len = len(word) if (word in self.words) or (word in self.delimiter): word_list.append(word) count = count + word_len word_len = self.max_len else: word_len = word_len - 1 return word_list

```

3.2 后向最大匹配

```python def reverse_mm_split(self, rmm_text): """ 逆向最大匹配分词算法 :param rmm_text: 待分词字符串 :return: 分词结果,以list形式存放,每个元素为分出的词 """ # 字词列表,存放分词结果 word_list = [] # 用于记录分词的末尾位置 count = len(rmm_text) # 字或词当前的长度 word_len = self.max_len while word_len > 0 and count > 0: if count <= word_len: word = rmm_text[:count] else: word = rmm_text[(count - word_len):count] word_len = len(word) if (word in self.words) or (word in self.delimiter): word_list.insert(0, word) count = count - word_len word_len = self.max_len else: word_len = word_len - 1 return word_list

```

3.3 双向最大匹配

```python def bidirectional_mm_split(self, bi_text): """ 双向最大匹配分词算法 :param bi_text: 待分词字符串 :return: 分词结果,以list形式存放,每个元素为分出的词 """ # 前向最大匹配得到的分词结果 forward = self.forward_mm_split(bi_text) # 后向最大匹配得到的分词结果 reverse = self.reverse_mm_split(bi_text) # 总词数 forward_total_words = len(forward) reverse_total_words = len(reverse) # 单字词个数 forward_single_words = 0 reverse_single_words = 0 # 非字典词数 forward_illegal_words = 0 reverse_illegal_words = 0 # 罚分,分值越低,表明结果越好 forward_score = 0 reverse_score = 0 if forward == reverse: return forward else: # 统计前向匹配的各个词情况 for word in forward: if len(word) == 1: forward_single_words += 1 if word not in self.words: forward_illegal_words += 1 # 统计后向匹配的各个词情况 for word in reverse: if len(word) == 1: reverse_single_words += 1 if word not in self.words: reverse_illegal_words += 1 # 计算罚分 if forward_total_words < reverse_total_words: reverse_score += 1 else: forward_score += 1 if forward_illegal_words < reverse_illegal_words: reverse_score += 1 else: forward_score += 1 if forward_single_words < reverse_single_words: reverse_score += 1 else: forward_score += 1 # 比较罚分情况,罚分最小的选做最终结果 if forward_score < reverse_score: return forward else: return reverse

```

4 运行结果

利用实验包已给出的代码框架,实现完对应的匹配函数后,运行测试样例。

1) 基于词典的中文分词,得到输出结果如下:

图 1- SEQ 图 \* ARABIC \s 1 1 中文分词测试结果

1) 基于统计学习的中文分词,将输出的分词文件进行在线测评,结果如下:

QQ图片20210601200158

图 1- SEQ 图 \* ARABIC \s 1 2 在线测评结果

5 实验小结

实验中总体的代码框架已给出,需要实现的部分为前向最大匹配、后向最大匹配和双向最大匹配三个核心函数。通过相关资料的参考以及对中文分词的个人理解,能够顺利实现各个匹配模式的实现。

基于统计学习的中文分词代码,由于已经给出了代码框架,所以只是在其基础上做了部分优化,并增加了一部分的训练数据。

实验中代码仅提供一种简单的 Bi-LSTM+CRF PyTorch 实现方案。

更优实现可参见:https://github.com/bamtercelboo/pytorch_NER_BiLSTM_CNN_CRF/

环境搭建

  1. 安装 Anaconda

windows 下安装教程

官方文档: anaconda install

  1. 搭建虚拟环境并安装 PyTorch

```shell # 创建虚拟环境 conda create -n nlplab python=3.7 # 创建名为 nlplab 的虚拟环境

# 虚拟环境相关命令 conda activate nlplab # 激活虚拟环境nlplab,成功执行后应看到命令行首部由 (base) 变为 (nlplab) conda deactivate # 退出当前虚拟环境 conda info -e # 查看所有虚拟环境,*指示当前所处环境

# 安装 Pytorch 1.6.0 CPU 版本 # 注意:先激活 nlplab 虚拟环境,再进行安装 conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/pytorch/ conda install pytorch==1.6.0 cpuonly ```

运行方式

  1. 配置 PyCharm

安装 PyCharm,并在 PyCharm 中使用 Anaconda 虚拟环境 ( 参考 )

  1. 安装其他依赖

sh # 在 nlplab 虚拟环境中安装 pip install -r requirements.txt

  1. 训练

```shell # save 目录下存放了一个粗略训练过的模型,可先跳过训练过程直接进行推断

# 数据准备,data 目录下运行 python 0.split.py python 1.data_u_ner.py # 模型训练,项目根目录下运行 # 若安装并配置了 GPU 相关运行环境可添加命令行参数 --cuda 来使用 GPU 训练 python run.py ```

  1. 推断

shell python infer.py

附录 中文分词实现的源程序
data_u.py

```python import pickle

from sklearn.model_selection import train_test_split

INPUT_DATA = "train.txt" SAVE_PATH = "./data_save.pkl" id2tag = ['B', 'M', 'E', 'S'] # B:分词头部 M:分词词中 E:分词词尾 S:独立成词 tag2id = {'B': 0, 'M': 1, 'E': 2, 'S': 3} word2id = {} id2word = []

def getlist(input_str): """ 单个分词转换为tag序列 :param input_str: 单个分词 :return: tag序列 """ out_str = [] if len(input_str) == 1: out_str.append(tag2id['S']) elif len(input_str) == 2: out_str = [tag2id['B'], tag2id['E']] else: m_num = len(input_str) - 2 m_list = [tag2id['M']] * m_num out_str.append(tag2id['B']) out_str.extend(m_list) out_str.append(tag2id['E']) return out_str

def handle_data(): """ 处理数据,并保存至save_path :return: """ x_data = [] y_data = [] word_num = 0 line_num = 0 with open(INPUT_DATA, 'r', encoding="utf-8") as ifp: for line in ifp: line_num = line_num + 1 line = line.strip() if not line: continue line_x = [] for i in range(len(line)): if line[i] == " ": continue if line[i] in id2word: line_x.append(word2id[line[i]]) else: id2word.append(line[i]) word2id[line[i]] = word_num line_x.append(word_num) word_num = word_num + 1 x_data.append(line_x)

        line_arr = line.split()
        line_y = []
        for item in line_arr:
            line_y.extend(getlist(item))
        y_data.append(line_y)

print(x_data[0])
print([id2word[i] for i in x_data[0]])
print(y_data[0])
print([id2tag[i] for i in y_data[0]])
x_train, x_test, y_train, y_test = train_test_split(x_data, y_data, test_size=0.1, random_state=43)
with open(SAVE_PATH, 'wb') as output:
    pickle.dump(word2id, output)
    pickle.dump(id2word, output)
    pickle.dump(tag2id, output)
    pickle.dump(id2tag, output)
    pickle.dump(x_train, output)
    pickle.dump(y_train, output)
    pickle.dump(x_test, output)
    pickle.dump(y_test, output)

if name == " main ": handle_data()

```

dataloader.py

```python import torch import pickle from torch.utils.data import Dataset, DataLoader from torch.nn.utils.rnn import pad_sequence

class Sentence(Dataset): def init (self, x, y, batch_size=10): self.x = x self.y = y self.batch_size = batch_size

def __len__(self):
    return len(self.x)

def __getitem__(self, idx):
    assert len(self.x[idx]) == len(self.y[idx])
    return self.x[idx], self.y[idx]

@staticmethod
def collate_fn(train_data):
    train_data.sort(key=lambda data: len(data[0]), reverse=True)
    data_length = [len(data[0]) for data in train_data]
    data_x = [torch.LongTensor(data[0]) for data in train_data]
    data_y = [torch.LongTensor(data[1]) for data in train_data]
    masks = [torch.ones(i, dtype=torch.uint8) for i in data_length]
    data_x = pad_sequence(data_x, batch_first=True, padding_value=0)
    data_y = pad_sequence(data_y, batch_first=True, padding_value=0)
    masks = pad_sequence(masks, batch_first=True, padding_value=0)
    return data_x, data_y, masks, data_length

if name == ' main ': # test with open('../data/data_save.pkl', 'rb') as inp: word2id = pickle.load(inp) id2word = pickle.load(inp) tag2id = pickle.load(inp) id2tag = pickle.load(inp) x_train = pickle.load(inp) y_train = pickle.load(inp) x_test = pickle.load(inp) y_test = pickle.load(inp)

train_dataloader = DataLoader(Sentence(x_train, y_train), batch_size=10, shuffle=True,
                              collate_fn=Sentence.collate_fn)

for input_data, label, mask, length in train_dataloader:
    print(input_data, label)
    break

```

infer.py

```python import torch import pickle

if name == ' main ': model = torch.load('save/model_epoch9.pkl', map_location=torch.device('cpu')) output = open('cws_result.txt', 'w', encoding='utf-8')

with open('data/data_save.pkl', 'rb') as inp:
    word2id = pickle.load(inp)
    id2word = pickle.load(inp)
    tag2id = pickle.load(inp)
    id2tag = pickle.load(inp)
    x_train = pickle.load(inp)
    y_train = pickle.load(inp)
    x_test = pickle.load(inp)
    y_test = pickle.load(inp)

with open('data/test_final.txt', 'r', encoding='utf-8') as f:
    for test in f:
        flag = False
        test = test.strip()

        x = torch.LongTensor(1, len(test))
        mask = torch.ones_like(x, dtype=torch.uint8)
        length = [len(test)]
        for i in range(len(test)):
            if test[i] in word2id:
                x[0, i] = word2id[test[i]]
            else:
                x[0, i] = len(word2id)

        predict = model.infer(x, mask, length)[0]
        for i in range(len(test)):
            print(test[i], end='', file=output)
            if id2tag[predict[i]] in ['E', 'S']:
                print(' ', end='', file=output)
        print(file=output)

```

model.py

```python import torch import torch.nn as nn from torchcrf import CRF from torch.nn.utils.rnn import pack_padded_sequence, pad_packed_sequence

class CWS(nn.Module):

def __init__(self, vocab_size, tag2id, embedding_dim, hidden_dim):
    super(CWS, self).__init__()
    self.embedding_dim = embedding_dim
    self.hidden_dim = hidden_dim
    self.vocab_size = vocab_size
    self.tag2id = tag2id
    self.tag_set_size = len(tag2id)

    self.word_embeds = nn.Embedding(vocab_size + 1, embedding_dim)

    self.lstm = nn.LSTM(embedding_dim, hidden_dim // 2, num_layers=1,
                        bidirectional=True, batch_first=True)
    self.hidden2tag = nn.Linear(hidden_dim, self.tag_set_size)

    self.crf = CRF(4, batch_first=True)

def init_hidden(self, batch_size, device):
    return (torch.randn(2, batch_size, self.hidden_dim // 2, device=device),
            torch.randn(2, batch_size, self.hidden_dim // 2, device=device))

def _get_lstm_features(self, sentence, length):
    batch_size, seq_len = sentence.size(0), sentence.size(1)

    # idx->embedding
    embeds = self.word_embeds(sentence.view(-1)).reshape(batch_size, seq_len, -1)
    embeds = pack_padded_sequence(embeds, length, batch_first=True)

    # LSTM forward
    self.hidden = self.init_hidden(batch_size, sentence.device)
    lstm_out, self.hidden = self.lstm(embeds, self.hidden)
    lstm_out, _ = pad_packed_sequence(lstm_out, batch_first=True)
    lstm_feats = self.hidden2tag(lstm_out)
    return lstm_feats

def forward(self, sentence, tags, mask, length):
    emissions = self._get_lstm_features(sentence, length)
    loss = -self.crf(emissions, tags, mask, reduction='mean')
    return loss

def infer(self, sentence, mask, length):
    emissions = self._get_lstm_features(sentence, length)
    return self.crf.decode(emissions, mask)

```

run.py

```python import pickle import logging import argparse import os import torch from torch.utils.data import DataLoader from torch.optim import Adam from model import CWS from dataloader import Sentence

def get_param(): parser = argparse.ArgumentParser() parser.add_argument('--embedding_dim', type=int, default=100) parser.add_argument('--lr', type=float, default=0.005) parser.add_argument('--max_epoch', type=int, default=10) parser.add_argument('--batch_size', type=int, default=128) parser.add_argument('--hidden_dim', type=int, default=200) parser.add_argument('--cuda', action='store_true', default=False) return parser.parse_args()

def set_logger(): log_file = os.path.join('save', 'log.txt') logging.basicConfig( format='%(asctime)s %(levelname)-8s %(message)s', level=logging.DEBUG, datefmt='%Y-%m%d %H:%M:%S', filename=log_file, filemode='w', )

console = logging.StreamHandler()
console.setLevel(logging.DEBUG)
formatter = logging.Formatter('%(asctime)s %(levelname)-8s %(message)s')
console.setFormatter(formatter)
logging.getLogger('').addHandler(console)

def entity_split(x, y, id2tag, entities, cur): start, end = -1, -1 for j in range(len(x)): if id2tag[y[j]] == 'B': start = cur + j elif id2tag[y[j]] == 'M' and start != -1: continue elif id2tag[y[j]] == 'E' and start != -1: end = cur + j entities.add((start, end)) start, end = -1, -1 elif id2tag[y[j]] == 'S': entities.add((cur + j, cur + j)) start, end = -1, -1 else: start, end = -1, -1

def main(args): use_cuda = args.cuda and torch.cuda.is_available()

with open('data/data_save.pkl', 'rb') as inp:
    word2id = pickle.load(inp)
    pickle.load(inp)
    tag2id = pickle.load(inp)
    id2tag = pickle.load(inp)
    x_train = pickle.load(inp)
    y_train = pickle.load(inp)
    x_test = pickle.load(inp)
    y_test = pickle.load(inp)

model = CWS(len(word2id), tag2id, args.embedding_dim, args.hidden_dim)
if use_cuda:
    model = model.cuda()
for name, param in model.named_parameters():
    logging.debug('%s: %s, require_grad=%s' % (name, str(param.shape), str(param.requires_grad)))

optimizer = Adam(model.parameters(), lr=args.lr)

train_data = DataLoader(
    dataset=Sentence(x_train, y_train),
    shuffle=True,
    batch_size=args.batch_size,
    collate_fn=Sentence.collate_fn,
    drop_last=False,
    num_workers=6
)

test_data = DataLoader(
    dataset=Sentence(x_test[:1000], y_test[:1000]),
    shuffle=False,
    batch_size=args.batch_size,
    collate_fn=Sentence.collate_fn,
    drop_last=False,
    num_workers=6
)

for epoch in range(args.max_epoch):
    step = 0
    log = []
    for sentence, label, mask, length in train_data:
        if use_cuda:
            sentence = sentence.cuda()
            label = label.cuda()
            mask = mask.cuda()

        # forward
        loss = model(sentence, label, mask, length)
        log.append(loss.item())

        # backward
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()

        step += 1
        if step % 100 == 0:
            logging.debug('epoch %d-step %d loss: %f' % (epoch, step, sum(log) / len(log)))
            log = []

    # test
    entity_predict = set()
    entity_label = set()
    with torch.no_grad():
        model.eval()
        cur = 0
        for sentence, label, mask, length in test_data:
            if use_cuda:
                sentence = sentence.cuda()
                label = label.cuda()
                mask = mask.cuda()
            predict = model.infer(sentence, mask, length)

            for i in range(len(length)):
                entity_split(sentence[i, :length[i]], predict[i], id2tag, entity_predict, cur)
                entity_split(sentence[i, :length[i]], label[i, :length[i]], id2tag, entity_label, cur)
                cur += length[i]

        right_predict = [i for i in entity_predict if i in entity_label]
        if len(right_predict) != 0:
            precision = float(len(right_predict)) / len(entity_predict)
            recall = float(len(right_predict)) / len(entity_label)
            logging.info("precision: %f" % precision)
            logging.info("recall: %f" % recall)
            logging.info("score: %f" % ((2 * precision * recall) / (precision + recall)))
        else:
            logging.info("precision: 0")
            logging.info("recall: 0")
            logging.info("score: 0")
        model.train()

    path_name = "./save/model_epoch" + str(epoch) + ".pkl"
    torch.save(model, path_name)
    logging.info("model has been saved in  %s" % path_name)

if name == ' main ': set_logger() main(get_param())

```

参考文献

  • 财经领域事件抽取技术的研究与应用(北京理工大学·陈贺)
  • 文本综合处理平台的研究与实现(济南大学·王孟孟)
  • 面向农业领域的智能问答系统设计与实现(南京林业大学·张文婕)
  • 分布式全文检索系统中索引管理及文件预处理研究(中国科学技术大学·戴上静)
  • 基于领域本体和模板逻辑的中英双语问答系统的研究(广西师范大学·毛俊青)
  • 分布式全文检索系统中索引管理及文件预处理研究(中国科学技术大学·戴上静)
  • 以就业为导向的中职计算机教学资源平台的设计与实现(河北科技大学·翟红丽)
  • 融入中文分词信息的共指消解模型(华东师范大学·肖巧文)
  • 基于自然语言理解的全文搜索研究(湖北大学·黄翠平)
  • 面向中文产品评论的情感分析研究(西南大学·孙雪峰)
  • 面向中文产品评论的情感分析研究(西南大学·孙雪峰)
  • 文本分类方法及其四险一金领域应用研究(哈尔滨工程大学·吴汉瑜)
  • 基于LEBERT和知识图谱的智能地址修正补全方法(暨南大学·林佳铎)
  • 基于K-Means的分布式文本聚类系统的设计与实现(西安电子科技大学·马婵媛)
  • Text Classification Based on Graph Convolutional Neural Network with Intimacy Matrix and Text Linking(华中师范大学·夏冰)

本文内容包括但不限于文字、数据、图表及超链接等)均来源于该信息及资料的相关主题。发布者:代码海岸 ,原文地址:https://m.bishedaima.com/yuanma/35740.html

相关推荐

  • 图像去雾Python

    图像去雾 一,总述 本次大作业要求调研实现去雾算法,发现其中的问题,并对算法进行改进, 我首先实现了基于暗原色先验的去雾算法,并从运算速度和去雾效果方面进行了一定的改进
    2024年05月14日
    2 1 1
  • 基于Python进行人脸验证人脸识别综合开发

    人脸识别 人脸识别系统通常被分成两大类: ① 人脸验证 :“这是不是本人”,需要通过刷身份证(或者能证明身份的有效证件)以及摄像头拍摄人脸照片
    2024年05月14日
    2 1 1
  • 基于SpringBoot框架的外贸crm系统

    这是一套采用Java语言构建的🔥🔥SpringBoot为核心的外贸CRM系统源代码,该项目运用了SpringBoot框架和Vue技术栈,开发工具为Idea或Eclipse
    2024年05月23日
    11 1 3
  • 基于springboot的客户关系管理系统

    这是一个🔥🔥基于springboot的客户关系管理系统🔥🔥的项目源码,开发语言Java,开发环境Idea/Eclipse,这个 客户关系管理(CRM)开发技术栈为SpringBoot项目
    2024年05月23日
    4 1 1
  • 基于Python和opencv实现抖音上墨镜和烟卷效果

    基于Python和opencv实现抖音上墨镜和烟卷效果 一,项目简介 现今较火的抖音上有一个十分有趣的特效,其可以自动检测出人脸并且放置墨镜和烟卷,鉴于此
    2024年05月14日
    2 1 1
  • 基于python的安全即时通讯系统

    Uchat——基于 python 的安全即时通讯系统 目的 设计完成简易的安全即时通讯系统,实现类似于 QQ 的聊天软件; 需求分析 功能需求 聊天客户端 注册:用户与集中服务器通信完成注册
    2024年05月14日
    22 1 2
  • 基于Java的交易订单管理系统

    基于Java的交易订单管理系统 摘 要 Java语言自1995年诞生至今,一直以简明严谨的结构,简洁的语法编写,对网络应用的支持和强大的稳健性及安全性而雄踞世界流行编程语言排行榜首
    2024年05月14日
    2 1 1
  • 在线兼职网

    这是一个🔥🔥基于SpringBoot框架的在线兼职网设计与实现🔥🔥的项目源码,开发语言Java,框架使用的SpringBoot+vue技术,开发环境Idea/Eclipse
    2024年05月23日
    3 1 1
  • 实现一个轻量级的 Web 服务器

    实现一个轻量级的 Web 服务器 实验目的 深入掌握 HTTP 协议规范,学习如何编写标准的互联网应用服务器, 实验内容 服务程序能够正确解析 HTTP 协议
    2024年05月14日
    2 1 1
  • 基于SpringBoot框架的医疗报销系统

    这是一套采用Java编程语言,基于SpringBoot框架构建的医疗报销管理系统源代码,项目中融入了Vue技术,开发工具为Idea或Eclipse,此系统适用于毕业设计或课程实践
    2024年05月23日
    9 1 2

发表回复

登录后才能评论