Stream 프로 그래 밍 의 N - gram 구현

4738 단어
N - gram 은 자주 사용 하 는 확률 언어 모델 로 기 존의 언어 자 료 를 통 해 문장 구조의 합 리 성 을 추정 할 수 있 고 자연 언어 처리 에서 광범 위 하 게 응용 되 며 N - gram 의 개념 은 더 이상 말 하지 않 고 인터넷 에 많은 튜 토리 얼 이 있 으 므 로 알 고 싶 은 것 은 스스로 검색 할 수 있다.Stream 은 자바 8 의 새로운 특성 입 니 다. 자바 8 은 발 표 된 지 3 년 이 넘 었 습 니 다. 여러분 들 이 실제 적 으로 얼마나 응용 되 었 는 지 모 르 겠 습 니 다. 작업 원인 은 2 년 동안 자바 코드 를 적 게 썼 기 때문에 N - gram 알고리즘 을 가지 고 연습 을 했 습 니 다. 개인 적 으로 stream 은 문자 처리 에 적합 하 다 고 생각 합 니 다. 스 트림 프로 그래 밍 은 쓰기 가 편리 합 니 다.구체 적 인 실현 을 살 펴 보 자.
package nlp.ngram;

import java.io.BufferedReader;
import java.io.File;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class Ngram {

    private static Map trainingData;

    static {
        String path = ".zhuxian.txt";
        trainingData = initTrainingData(path);//       ,     
    }

    /**
     *             
     * @param sentence       
     * @param n N-gram N
     * @return      
     */
    private static float getProbability(String sentence, int n) {
        final String sen = "@" + sentence + "#";
        return Stream
            .iterate(0, i -> ++i)
            .limit(sen.length() - n + 1)
            .map(start -> sen.substring(start, start + n))
            .map(s -> {
                float nu = (float) (null == trainingData.get(s) ? 1 : trainingData.get(s).get() + 1);
                float de = (float) (null == trainingData.get(s.substring(0, s.length() - 1)) ? 1 : trainingData.get
                    (s.substring(0, s.length() - 1)).get() + 1);
                System.out.println(s + "/" + s.substring(0, s.length() - 1) + " " + nu / de);
                return nu / de;
            })
            .reduce(1f, (f1, f2) -> f1 * f2);
    }

    /**
     *               ,           
     * @param path      
     * @return     map
     */
    private static Map initTrainingData(String path) {
        return readFileOrDir(path)
            .map(s -> s.replaceAll("[”“\\w\\s《》.::*‘’、\"<>\\[\\]^`~]", ""))//           ,       
            .flatMap(s -> Stream.of(s.split("[,,。;;!!??]")))//    
            .filter(s -> !"".equals(s))//    
//          .peek(System.out::println)
            .map(s -> "@" + s + "#")//         
            .flatMap(s -> Stream
                .iterate(1, i -> ++i)//   N-gram N 1、2、3、4
                .limit(s.length() > 4 ? 4 : s.length())//N-gram N   4,        ,     4     
                .parallel()
                .flatMap(n -> Stream
                    .iterate(0, i -> ++i)
                    .limit(s.length() - n + 1)
                    .parallel()
                    .map(start -> s.substring(start, start + n))//     n     
                )
            )
            .collect(Collectors.toConcurrentMap(o -> o,
                o -> new AtomicInteger(1), (e1, e2) -> {
                e1.incrementAndGet();
                return e1;
            }));
    }

    /**
     *       ,       
     * @param path     
     * @return Stream lines  
     */
    private static Stream readFileOrDir(String path) {
        File file = new File(path);
        if (file.isDirectory()) {
            String[] paths = file.list((dir, name) -> !name.startsWith("."));
            assert paths != null;
            return Arrays.stream(paths)
                .flatMap(p -> readFileOrDir(path + File.separator + p));
        } else {
            try {
                return new BufferedReader(new java.io.FileReader(path)).lines();
            } catch (Exception e) {
                System.err.println("read file " + path + " error!" + e.getMessage());
                return Stream.empty();
            }
        }
    }

    public static void main(String[] args) throws Exception {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        br.lines().forEach(s -> System.out.println(getProbability(s, 3)));
    }
}

여기 서 자바 스 트림 프로 그래 밍 을 사용 하면 코드 의 양 을 크게 줄 일 수 있 습 니 다. 한 방법 으로 언어 자 료 를 처리 할 수 있 습 니 다. 한 방법 으로 확률 을 계산 하면 끝 납 니 다. cpu 밀집 형 작업 에 대해 서 는 다 중 스 레 드 (parallel) 로 처리 속 도 를 가속 화 할 수 있 지만 동시에 발생 하 는 구 덩이 는 스스로 채 워 야 합 니 다.여기 서 의 실현 은 상당 한 기초 가 있 고 단어 가 없 으 면 정확성 이 많이 낮 을 것 이 며 최적화 할 수 있 는 공간 이 매우 크다. 그리고 데이터 의 부 드 러 운 (smoothing) 처 리 는 여기 서 토론 을 하지 않 는 다. 여기 서 의 실현 은 나타 나 지 않 은 단어 가 나타 나 는 횟수 를 간단하게 1 로 설정 하고 실제 사용 할 때 실제 디자인 데이터 의 부 드 러 운 알고리즘 을 결합 시 켜 최적화 된 부분 을 다시 채 울 시간 이 있다.

좋은 웹페이지 즐겨찾기